I am currently on Angular/Infragistics 12.0.3.
I have a data grid as below:
<igx-grid #designFacilities displayDensity="compact" [data]="dfMappings" [paging]="true" [perPage]="25" [allowFiltering]="true" [allowAdvancedFiltering]="true" [primaryKey]="'id'" width="695px" height="{{height}}px" [rowEditable]="globals.user.isContributor" (rowEditDone)="rowEditedDone($event)" [filterMode]="'excelStyleFilter'" style="margin: 0 auto;"> <igx-grid-toolbar class="bg-primary"> <igx-grid-toolbar-title class="text-white"><b>Design Facility Mappings</b></igx-grid-toolbar-title> </igx-grid-toolbar> <igx-column field="facility" header="Facility" [dataType]="'string'" [resizable]="true" width="97px" [editable]="false" [sortable]="true"></igx-column> <igx-column field="design" header="Design" [dataType]="'string'" [resizable]="true" width="109px" [editable]="false" [sortable]="true"></igx-column> <igx-column field="isVisible" header="Visible" [dataType]="'boolean'" [resizable]="true" width="94px"></igx-column> <igx-column field="isPrivate" header="Private" [dataType]="'boolean'" [resizable]="true" width="96px"></igx-column> <igx-column field="isTd" header="TD" [dataType]="'boolean'" [resizable]="true" width="71px"></igx-column> <igx-column header="Actions" [filterable]="false" [editable]="false" width="210px" [resizable]="true" [sortable]="false"> <ng-template igxCell let-cell="cell" let-val> <button [hidden]="scenario.scenarioType == 'Actual' || !globals.user.isContributor" igxButton (click)="deleteMapping(cell.rowData)">Delete</button> <button [hidden]="scenario.scenarioType == 'Actual' || !globals.user.isContributor" igxButton (click)="setStartData(cell.rowData); copyYield.open()">Copy Yield</button> </ng-template> </igx-column> </igx-grid>
I use a modal to add new rows as below:
<igx-dialog #addDfMapping [closeOnOutsideSelect]="true"> <igx-dialog-title> <div class="dialog-title">Add Design-Facility Mapping</div> </igx-dialog-title> <div *ngIf="designs !== undefined && designs.length > 50 && newDf !== undefined && newDf.design !== undefined"> <igx-input-group #designGroup [igxToggleAction]="design" displayDensity="compact"> <igx-prefix>Design: </igx-prefix> <input #designInput type="text" igxInput [igxDropDownItemNavigation]="design" readonly="true" [(ngModel)]="newDf.design" [value]="design.selectedItem?.value" (keydown.ArrowDown)="openDropDown('design')" /> <igx-suffix igxButton="icon" class="dropdownToggleButton" igxRipple> <igx-icon>arrow_drop{{ design.collapsed ? '_down' : '_up' }}</igx-icon> </igx-suffix> </igx-input-group> <igx-drop-down #design (onSelection)="filterFacilities($event.newSelection.value)" displayDensity="compact"> <div class="scrollable"> <igx-drop-down-item *ngFor="let des of designs" [selected]="des.name == newDf.design" [value]="des.name">{{des.name}}</igx-drop-down-item> </div> </igx-drop-down> </div> <div *ngIf="facilities !== [] && filteredFacilities !== [] && newDf !== undefined && newDf.facility !== undefined"> <igx-input-group #facilityGroup [igxToggleAction]="facility" displayDensity="compact"> <igx-prefix>Facility: </igx-prefix> <input #facilityInput type="text" igxInput [igxDropDownItemNavigation]="facility" readonly="true" [(ngModel)]="newDf.facility" [value]="facility.selectedItem?.value" (keydown.ArrowDown)="openDropDown('facility')" /> <igx-suffix igxButton="icon" class="dropdownToggleButton" igxRipple> <igx-icon>arrow_drop{{ facility.collapsed ? '_down' : '_up' }}</igx-icon> </igx-suffix> </igx-input-group> <igx-drop-down #facility (onSelection)="updateSelectedFacility($event.newSelection.value)" displayDensity="compact"> <div class="scrollable"> <igx-drop-down-item *ngFor="let fac of filteredFacilities" [selected]="fac.name == newDf.facility" [value]="fac.name">{{fac.name}}</igx-drop-down-item> </div> </igx-drop-down> </div> <br> <div style="display: inline-block;"> <igx-checkbox *ngIf="newDf !== undefined && newDf.isVisible !== undefined" [(ngModel)]="newDf.isVisible" labelPosition="before">Visible: </igx-checkbox> <igx-checkbox *ngIf="newDf !== undefined && newDf.isPrivate !== undefined" [(ngModel)]="newDf.isPrivate" labelPosition="before">Private: </igx-checkbox> <igx-checkbox *ngIf="newDf !== undefined && newDf.isTD !== undefined" [(ngModel)]="newDf.isTD" labelPosition="before">TD: </igx-checkbox> </div> <br><br> <button igxButton="outlined" (click)="addDfMapping.close(); createDesignFacilityMapping()"><igx-icon family="material">check</igx-icon><span>Add DF Mapping</span></button> <button igxButton="outlined" (click)="addDfMapping.close()"><igx-icon family="material">cancel</igx-icon><span>Cancel</span></button> </igx-dialog>
Adding a single mapping seems to work just fine. Here are relatable functions:
@ViewChild('designFacilities', { static: false, read: IgxGridComponent }) public dfMappingsList: IgxGridComponent; @ViewChild('design', { static: false, read: IgxDropDownComponent }) public designDd: IgxDropDownComponent; @ViewChild('designGroup', { static: false, read: IgxInputGroupComponent }) public designGroup: IgxInputGroupComponent; @ViewChild('facility', { static: false, read: IgxDropDownComponent }) public facilityDd: IgxDropDownComponent; @ViewChild('facilityGroup', { static: false, read: IgxInputGroupComponent }) public facilityGroup: IgxInputGroupComponent; public openDropDown(dd:string) { if (dd == 'design') { if (this.designDd.collapsed) { this.designDd.open({ target: this.designGroup.element.nativeElement, modal: false, positionStrategy: new ConnectedPositioningStrategy() }); } } else if (dd == 'facility') { if (this.facilityDd.collapsed) { this.facilityDd.open({ target: this.facilityGroup.element.nativeElement, modal: false, positionStrategy: new ConnectedPositioningStrategy() }); } } } filterFacilities(design:string) { this.filteredFacilities = []; this.newDf.design = design, this.newDf.designId = this.designs.filter(d => d.name == design)[0].id; this.filteredFacilities = this.facilities.filter(a => this.designs.filter(d => d.name == design)[0].facilities.indexOf(a.name) > -1); this.newDf.facility = this.filteredFacilities[0].name, this.newDf.facilityId = this.filteredFacilities[0].id; }; updateSelectedFacility(name:string) { this.newDf.facility = name; this.newDf.facilityId = this.facilities.filter(f => f.name == name)[0].id; }; async createDesignFacilityMapping() { let response:any = await this.scenarioService.createDesignFacilityMapping(this.newDf).toPromise(); if (response.statusCode == 200) { console.log(`Mapping ${this.newDf.design} for ${this.newDf.facility} successfully created!`); this.dfMappings.push(this.newDf); this.dfMappings.sort((a, b) => a.design < b.design ? -1 : a.design > b.design ? 1 : 0); this.dfMappingsList.markForCheck(); this.reportChanges.emit('mapping created'); } else { console.log(`Failed to create mapping ${this.newDf.design} for ${this.newDf.facility}: `, response); } };
When I add another mapping, however, the facility name in the grid for the first mapping that was added reverts back to the first facility in the appropriately filtered facilities for that design.
This change happens for all added mappings, as I add more to the grid.
When updated (loc.reload() or F5, for instance), the data is as it was added, rather than the changed values that propagated in the grid.
Users will get confused when adding multiple mappings, so obviously this behavior must be curbed.
Thanks in advance for your assistance.
I think as a normal course of making updates and refining code this has been resolved, although I cannot say for sure exactly how. For informational purposes, I will show the flow here:
Facility change in dropdown is recorded in newDf:
updateSelectedFacility(name:string) { this.newDf.facility = name; this.newDf.facilityId = this.facilities.filter(f => f.name == name)[0].id; };
When user clicks "add", newDf is sent to back-end for addition and the list is updated/re-sorted (mapping ID is updated only after user refreshes):
async createDesignFacilityMapping() { let response:any = await this.scenarioService.createDesignFacilityMapping(this.newDf).toPromise(); if (response.statusCode == 200) { console.log(`Mapping ${this.newDf.design} for ${this.newDf.facility} successfully created!`); this.newDf.id = `${this.newDf.design} for ${this.newDf.facility}`; this.dfMappings.push(this.newDf); this.dfMappings.sort((a, b) => { if (a.facility !== b.facility) { return a.facility.replace('2', '02') < b.facility.replace('2', '02') ? -1 : a.facility.replace('2', '02') > b.facility.replace('2', '02') ? 1 : 0; } else { return a.design < b.design ? -1 : a.design > b.design ? 1 : 0; } }); this.dfMappingsList.markForCheck(); this.reportChanges.emit('mapping created'); } else { console.log(`Failed to create mapping ${this.newDf.design} for ${this.newDf.facility}: `, response); } };
Hello Chris,
Thank you for getting back to me!
The format of the Facilities dropdown could be a possible reason as well. Take your time to prepare the working sample and provide it within this forum thread. As soon as I get it I will start debugging it and let you know regarding my findings.
In case you find an additional issue please do not hesitate to create a new forum thread regarding it.
Looking forward to hearing from you.
Best Regards, Martin Evtimov Associate Software Developer Infragistics, Inc.
I have confirmed it is not an issue with RowEditDone (as it's not touched during row adding via the Modal Dialog) and, although it looked like a good potential cause, the Unique Primary Key (potential issue) is also not the root cause here. I am working on reproducing it in a StackBlitz for you.
I suspect the issue may reside with the format of the Facilities dropdown, which is filtered based on the existing Design-Facility mappings and the design to be added. I also suspect this will be another ticket raised with Ignite UI as a result of my work.
Please take your time to test the rowEditDone function and let me know about your findings. Another possible reason for this behavior could be duplication of row primary keys.
In case that there isn’t any duplication of primary keys and that the rowEditDone function works as expected I will suggest providing me with a working sample reproducing the described behavior which I can debug on my side in order to provide you with solution as soon as possible.
rowEditDone:
rowEditedDone(e:any) { this.updateDesignFacilityMapping(e.newValue); };
Type of data source is SQL Server -> .NET CORE class matching the interface:
export interface scenarioDesignFacility { id:string, scenarioId:string, designId: string, design:string, facilityId:string, facility:string, isVisible:boolean, isPrivate:boolean, isTD:boolean }
ScenarioService strictly passes data between API and UI using the above interface (other than delete, which only passes the id:string).
The only validation that is happening is when facilities are filtered, a function which I already provided.
My assumption now is that the error may be propagating within the rowEditDone function, which I will test shortly. Thank you for the direction and pointing out something that was right in front of me.