Skip to content

fix(grids): keep custom row selector checkbox in sync - master#17429

Open
Zneeky wants to merge 2 commits into
masterfrom
aahmedov/fix-grid-row-selector-cd
Open

fix(grids): keep custom row selector checkbox in sync - master#17429
Zneeky wants to merge 2 commits into
masterfrom
aahmedov/fix-grid-row-selector-cd

Conversation

@Zneeky

@Zneeky Zneeky commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Description

The row now owns the custom selector view and rebuilds it whenever the row
starts showing a different record (its key changes) or the template changes. A
fresh view means a fresh checkbox, so the first binding write always lands on
clean DOM and cannot be stale. When the same record stays in place, the view is
kept and only its context is refreshed, so programmatic selection changes such
as select all or cascade still show correctly.

Key points:

  1. Only the custom selector path changed. The default IgxCheckbox selector
    renders exactly as before, so grids without a custom template see no change
    in behavior or performance.
  2. The rebuild happens once per record, not on every change detection pass, so
    it stays cheap.
  3. The same base row directive is shared by all three grid types, so one fix
    covers IgxGrid, IgxTreeGrid and IgxHierarchicalGrid.

Motivation / Context

Closes #17292

Summary

When a grid uses a custom igxRowSelector template containing a native checkbox,
the checkbox could show the wrong checked state after selecting, sorting or
scrolling. The selection data was always correct; only the rendered checkbox
was stale. Fixed for IgxGrid, IgxTreeGrid and IgxHierarchicalGrid.

Type of Change (check all that apply):

  • Bug fix
  • New functionality
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (no functional changes)
  • Documentation
  • Demos
  • CI/CD
  • Tests
  • Changelog
  • Skills/Agents

Component(s) / Area(s) Affected:

How Has This Been Tested?

  • Unit tests
  • Manual testing
  • Automated e2e tests

Test Configuration:

  • Angular version:
  • Browser(s):
  • OS:

Screenshots / Recordings

Checklist:

  • All relevant tags have been applied to this PR
  • This PR includes unit tests covering all the new code (test guidelines)
  • This PR includes API docs for newly added methods/properties (api docs guidelines)
  • This PR includes feature/README.MD updates for the feature docs
  • This PR includes general feature table updates in the root README.MD
  • This PR includes CHANGELOG.MD updates for newly added functionality
  • This PR contains breaking changes
  • This PR includes ng update migrations for the breaking changes (migrations guidelines)
  • This PR includes behavioral changes and the feature specification has been updated with them
  • Accessibility (ARIA, keyboard navigation, focus management) has been verified

A custom igxRowSelector template that uses a native <input type="checkbox">
kept a stale checked state when the grid recycled the row's DOM for a
different record. The row now owns the selector view and re-creates it
whenever it is reused for a new record, so the checkbox is always rendered
against fresh DOM. Applies to igx-grid, igx-tree-grid and
igx-hierarchical-grid.

Also adds a dev-demo repro page (grid-row-selector-cd) for review; it is
marked for testing purposes only and can be removed afterwards.

Closes #17292

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a row-virtualization rendering issue where custom igxRowSelector templates containing interactive native elements (e.g. <input type="checkbox">) can display a stale checked state after rows are recycled for different records. The change moves ownership of custom row-selector rendering into the row directive, allowing it to recreate the embedded view when the row’s record key or template changes, while only refreshing context when the same record remains.

Changes:

  • Render custom row selector templates via a row-owned ViewContainerRef, recreating the embedded view on record key/template changes to avoid stale DOM state.
  • Update grid/hierarchical/tree row templates to use an outlet container for custom selectors while keeping the default selector path unchanged.
  • Add a unit test and a demo-app manual repro page/route for #17292.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
projects/igniteui-angular/grids/core/src/row.directive.ts Adds custom row selector view creation/caching logic keyed by record + template.
projects/igniteui-angular/grids/grid/src/grid-row.component.html Switches custom selector rendering to an outlet container (#rowSelectorOutlet).
projects/igniteui-angular/grids/tree-grid/src/tree-grid-row.component.html Same outlet-based custom selector rendering for tree grid rows.
projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-row.component.html Same outlet-based custom selector rendering for hierarchical grid rows.
projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-row.component.ts Overrides row selector context to re-add select/deselect for hierarchical rows.
projects/igniteui-angular/grids/grid/src/grid-row-selection.spec.ts Adds a regression test intended to validate checkbox state sync for custom selectors.
src/app/grid-row-selector-cd/grid-row-selector-cd.sample.ts Adds a demo-app manual repro page for #17292 across grid types.
src/app/app.routes.ts Wires the new manual repro page into the demo app routing.
src/app/app.component.ts Adds a navigation entry for the new manual repro page.

Comment on lines +538 to +548
if (!this._rowSelectorViewRef || this._rowSelectorViewRef.destroyed
|| template !== this._rowSelectorViewTemplate || this.key !== this._rowSelectorViewKey) {
outlet.clear();
this._rowSelectorViewTemplate = template;
this._rowSelectorViewKey = this.key;
this._rowSelectorViewRef = outlet.createEmbeddedView(template, this.rowSelectorContext);
} else {
// Same record - refresh the context snapshot so selection changes are reflected.
this._rowSelectorViewRef.context.$implicit = this.rowSelectorContext.$implicit;
}
this._rowSelectorViewRef.detectChanges();

public onSelectionChanging(event: IRowSelectionEventArgs): void {
// Keep our signal in sync with the grid selection -> re-computes & re-sorts the data.
this.selectedIds.set(event.newSelection.map((r: Person) => r.ID));
Comment on lines +2389 to +2394
it('Should reflect the selection state in a custom row selector checkbox (#17292)', () => {
const getCheckbox = () => GridSelectionFunctions.getRowCheckboxInput(grid.gridAPI.get_row_by_index(0).nativeElement);

grid.selectRows(['ALFKI']);
fix.detectChanges();
expect(getCheckbox().checked).toBe(true);
Comment thread src/app/app.routes.ts
Comment on lines +534 to +537
{
path: 'gridRowSelectorCd',
component: GridRowSelectorCdSampleComponent
},
Comment thread src/app/app.component.ts
Comment on lines +437 to +441
{
link: '/gridRowSelectorCd',
icon: 'view_column',
name: 'Grid Row Selector CD (#17292)'
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IgxGrig. ChangeDetection Issue with a custom igxRowSelector Template

2 participants