Skip to content

Commit dd7bdfa

Browse files
Merge pull request #14413 from IgniteUI/bpachilova/comboIdChangeInTemplate-fix-14305-18.0.x
fix(combo): Handle selection when placed in an IgxGrid cell template and grid is virtualized - 18.0.x
2 parents 71057de + f72591b commit dd7bdfa

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

projects/igniteui-angular/src/lib/combo/combo.common.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,27 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh
167167
*/
168168
@HostBinding('attr.id')
169169
@Input()
170-
public id = `igx-combo-${NEXT_ID++}`;
170+
public get id(): string {
171+
return this._id;
172+
}
173+
174+
public set id(value: string) {
175+
if (!value) {
176+
return;
177+
}
178+
const selection = this.selectionService.get(this._id);
179+
this._id = value;
180+
if (selection) {
181+
this.selectionService.set(this._id, selection);
182+
}
183+
if (this.dropdown.open) {
184+
this.dropdown.close();
185+
}
186+
if (this.inputGroup?.isFocused) {
187+
this.inputGroup.element.nativeElement.blur();
188+
this.inputGroup.isFocused = false;
189+
}
190+
}
171191

172192
/**
173193
* Sets the style width of the element
@@ -943,6 +963,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh
943963
protected compareCollator = new Intl.Collator();
944964
protected computedStyles;
945965

966+
private _id: string = `igx-combo-${NEXT_ID++}`;
946967
private _type = null;
947968
private _dataType = '';
948969
private _itemHeight = null;

projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts

+103
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { AbsoluteScrollStrategy, AutoPositionStrategy, ConnectedPositioningStrat
1616
import { configureTestSuite } from '../test-utils/configure-suite';
1717
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
1818
import { IgxSimpleComboComponent, ISimpleComboSelectionChangingEventArgs } from './public_api';
19+
import { IgxGridComponent } from '../grids/grid/grid.component';
20+
import { IGX_GRID_DIRECTIVES } from '../grids/grid/public_api';
1921

2022

2123
const CSS_CLASS_COMBO = 'igx-combo';
@@ -2447,8 +2449,109 @@ describe('IgxSimpleCombo', () => {
24472449
expect(input.nativeElement.value).toEqual('Product 5');
24482450
}));
24492451
});
2452+
2453+
describe('Integration', () => {
2454+
let grid: IgxGridComponent;
2455+
2456+
beforeAll(waitForAsync(() => {
2457+
TestBed.configureTestingModule({
2458+
imports: [
2459+
NoopAnimationsModule,
2460+
IgxSimpleComboInGridComponent
2461+
]
2462+
}).compileComponents();
2463+
}));
2464+
beforeEach(() => {
2465+
fixture = TestBed.createComponent(IgxSimpleComboInGridComponent);
2466+
fixture.detectChanges();
2467+
grid = fixture.componentInstance.grid;
2468+
});
2469+
it('Combo in IgxGrid cell display template correctly handles selection - issue #14305', async () => {
2470+
const firstRecRegionCell = grid.gridAPI.get_cell_by_index(0, 'Region') as any;
2471+
let comboNativeEl = firstRecRegionCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT);
2472+
const comboToggleButton = comboNativeEl.querySelector(`.${CSS_CLASS_TOGGLEBUTTON}`);
2473+
2474+
UIInteractions.simulateClickEvent(comboToggleButton);
2475+
fixture.detectChanges();
2476+
2477+
const comboDropDownList = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROPDOWNLIST}`));
2478+
const firstItem = comboDropDownList.nativeElement.querySelector(`.${CSS_CLASS_DROPDOWNLISTITEM}`);
2479+
2480+
UIInteractions.simulateClickEvent(firstItem);
2481+
fixture.detectChanges();
2482+
2483+
const firstRegionCellObject = grid.getCellByColumn(0, 'Region');
2484+
expect(firstRegionCellObject.value).toEqual(fixture.componentInstance.regions[0]);
2485+
2486+
try {
2487+
// combo should not throw from the selection getter at this point
2488+
grid.navigateTo(fixture.componentInstance.data.length - 1, 0);
2489+
await wait(30);
2490+
fixture.detectChanges();
2491+
} catch (error) {
2492+
fail(`Test failed with error: ${error}`)
2493+
}
2494+
2495+
const virtState = grid.verticalScrollContainer.state;
2496+
expect(virtState.startIndex).toBe(grid.dataView.length - virtState.chunkSize);
2497+
2498+
// These will fail in case the editor (combo) in the cell display template is not bound to the cell value
2499+
// as the first record's selected value will be applied on the reused combos bc of the virtualization
2500+
for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) {
2501+
const targetCell = grid.gridAPI.get_cell_by_index(i, 'Region') as any;
2502+
comboNativeEl = targetCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT);
2503+
const comboInput = comboNativeEl.querySelector('input');
2504+
expect(comboInput.value).toBe('', `Failed on index: ${i.toString()}`);
2505+
}
2506+
2507+
for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) {
2508+
const cell = grid.getCellByColumn(i, 'Region');
2509+
expect(cell.value).toBe(undefined);
2510+
}
2511+
});
2512+
});
24502513
});
24512514

2515+
@Component({
2516+
template: `
2517+
<igx-grid #grid [data]="data" [autoGenerate]="false" width="100%" height="500px" [primaryKey]="'ID'">
2518+
<igx-column field="ID" header="ID" [dataType]="'number'" width="100px">
2519+
</igx-column>
2520+
<igx-column field="Region" header="Region" dataType="string" width="220px">
2521+
<ng-template igxCell let-cell="cell">
2522+
<div>
2523+
<igx-simple-combo [id]="'region-' + cell.row.data.ID"
2524+
[data]="regions" placeholder="Choose Region..."
2525+
[(ngModel)]="cell.value"
2526+
>
2527+
</igx-simple-combo>
2528+
</div>
2529+
</ng-template>
2530+
</igx-column>
2531+
</igx-grid>
2532+
`,
2533+
standalone: true,
2534+
imports: [IgxSimpleComboComponent, IGX_GRID_DIRECTIVES, FormsModule]
2535+
})
2536+
class IgxSimpleComboInGridComponent {
2537+
@ViewChild('grid', { read: IgxGridComponent, static: true })
2538+
public grid: IgxGridComponent;
2539+
2540+
public data = [];
2541+
public regions = [];
2542+
constructor() {
2543+
for (let i = 1; i <= 15; i++) {
2544+
this.data.push({
2545+
ID: i,
2546+
region: undefined
2547+
});
2548+
}
2549+
for (let i = 1; i <= 5; i++) {
2550+
this.regions.push(`Region ${i}`);
2551+
}
2552+
}
2553+
}
2554+
24522555
@Component({
24532556
template: `
24542557
<igx-simple-combo #combo [placeholder]="'Location'" [data]='items' [style.--ig-size]="'var(--ig-size-' + size + ')'"

0 commit comments

Comments
 (0)