diff --git a/projects/igniteui-angular/src/lib/combo/combo.common.ts b/projects/igniteui-angular/src/lib/combo/combo.common.ts index 91cf7852566..d2e1075b2cf 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.common.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.common.ts @@ -167,7 +167,27 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh */ @HostBinding('attr.id') @Input() - public id = `igx-combo-${NEXT_ID++}`; + public get id(): string { + return this._id; + } + + public set id(value: string) { + if (!value) { + return; + } + const selection = this.selectionService.get(this._id); + this._id = value; + if (selection) { + this.selectionService.set(this._id, selection); + } + if (this.dropdown.open) { + this.dropdown.close(); + } + if (this.inputGroup?.isFocused) { + this.inputGroup.element.nativeElement.blur(); + this.inputGroup.isFocused = false; + } + } /** * Sets the style width of the element @@ -943,6 +963,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh protected compareCollator = new Intl.Collator(); protected computedStyles; + private _id: string = `igx-combo-${NEXT_ID++}`; private _type = null; private _dataType = ''; private _itemHeight = null; diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts index c3cecdffbff..1f93197de61 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts @@ -16,6 +16,8 @@ import { AbsoluteScrollStrategy, AutoPositionStrategy, ConnectedPositioningStrat import { configureTestSuite } from '../test-utils/configure-suite'; import { UIInteractions, wait } from '../test-utils/ui-interactions.spec'; import { IgxSimpleComboComponent, ISimpleComboSelectionChangingEventArgs } from './public_api'; +import { IgxGridComponent } from '../grids/grid/grid.component'; +import { IGX_GRID_DIRECTIVES } from '../grids/grid/public_api'; const CSS_CLASS_COMBO = 'igx-combo'; @@ -2563,8 +2565,109 @@ describe('IgxSimpleCombo', () => { expect(input.nativeElement.value).toEqual('Product 5'); })); }); + + describe('Integration', () => { + let grid: IgxGridComponent; + + beforeAll(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + IgxSimpleComboInGridComponent + ] + }).compileComponents(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(IgxSimpleComboInGridComponent); + fixture.detectChanges(); + grid = fixture.componentInstance.grid; + }); + it('Combo in IgxGrid cell display template correctly handles selection - issue #14305', async () => { + const firstRecRegionCell = grid.gridAPI.get_cell_by_index(0, 'Region') as any; + let comboNativeEl = firstRecRegionCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT); + const comboToggleButton = comboNativeEl.querySelector(`.${CSS_CLASS_TOGGLEBUTTON}`); + + UIInteractions.simulateClickEvent(comboToggleButton); + fixture.detectChanges(); + + const comboDropDownList = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROPDOWNLIST}`)); + const firstItem = comboDropDownList.nativeElement.querySelector(`.${CSS_CLASS_DROPDOWNLISTITEM}`); + + UIInteractions.simulateClickEvent(firstItem); + fixture.detectChanges(); + + const firstRegionCellObject = grid.getCellByColumn(0, 'Region'); + expect(firstRegionCellObject.value).toEqual(fixture.componentInstance.regions[0]); + + try { + // combo should not throw from the selection getter at this point + grid.navigateTo(fixture.componentInstance.data.length - 1, 0); + await wait(30); + fixture.detectChanges(); + } catch (error) { + fail(`Test failed with error: ${error}`) + } + + const virtState = grid.verticalScrollContainer.state; + expect(virtState.startIndex).toBe(grid.dataView.length - virtState.chunkSize); + + // These will fail in case the editor (combo) in the cell display template is not bound to the cell value + // as the first record's selected value will be applied on the reused combos bc of the virtualization + for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) { + const targetCell = grid.gridAPI.get_cell_by_index(i, 'Region') as any; + comboNativeEl = targetCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT); + const comboInput = comboNativeEl.querySelector('input'); + expect(comboInput.value).toBe('', `Failed on index: ${i.toString()}`); + } + + for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) { + const cell = grid.getCellByColumn(i, 'Region'); + expect(cell.value).toBe(undefined); + } + }); + }); }); +@Component({ + template: ` + + + + + +
+ + +
+
+
+
+ `, + standalone: true, + imports: [IgxSimpleComboComponent, IGX_GRID_DIRECTIVES, FormsModule] +}) +class IgxSimpleComboInGridComponent { + @ViewChild('grid', { read: IgxGridComponent, static: true }) + public grid: IgxGridComponent; + + public data = []; + public regions = []; + constructor() { + for (let i = 1; i <= 15; i++) { + this.data.push({ + ID: i, + region: undefined + }); + } + for (let i = 1; i <= 5; i++) { + this.regions.push(`Region ${i}`); + } + } +} + @Component({ template: `