Skip to content

fix(grid): fix zone.onStable patterns broken in zoneless change detection - 21.2.x#17387

Open
viktorkombov wants to merge 1 commit into
21.2.xfrom
vkombov/grid-zoneless-issues-21.2.x
Open

fix(grid): fix zone.onStable patterns broken in zoneless change detection - 21.2.x#17387
viktorkombov wants to merge 1 commit into
21.2.xfrom
vkombov/grid-zoneless-issues-21.2.x

Conversation

@viktorkombov

@viktorkombov viktorkombov commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Closes #17364
Closes #17385
Closes #17318
Closes #17280

What

Grid virtualization, autosizing, and keyboard navigation relied on NgZone.onStable, which never emits in zoneless apps. As a result, scroll-driven chunkLoad, column autosize, filter-row rendering, and post-scroll cell activation silently never ran.

This migrates all of that off onStable and onto Angular’s render hooks.

Changes

Core

  • Added a new central helper: runAfterRenderOnce(injector, cb, phase?) in core/utils.ts.

    • This is now the single scheduling point for all deferred-render work.
    • Previously, this logic was scattered across onStable / ɵNoopNgZone branches.
  • Updated every affected call site to go through runAfterRenderOnce.

  • Removed the private ɵNoopNgZone-based zoneless detection.

  • Removed the duplicated per-class runAfterRender helpers.

Grids

  • IgxFilteringService.isFilterRowVisible is now signal-backed, so the OnPush header row re-renders without requiring a zone tick.
  • Filter cells now observe their own size via ResizeObserver to recompute visible chips after resize.
  • Column resize now explicitly notifies the grid via notifyResized, instead of relying on an empty zone.run.
  • Keyboard navigation now notifies after any scrolling navigation key, not just Ctrl.
  • Hierarchical navigation now subscribes to chunkLoad before scrolling, avoiding missed synchronous emissions.
  • Deferred work in for_of, grid-base, and pivot-grid was migrated to runAfterRenderOnce.

Tests

  • Added zoneless duplicates using provideZonelessChangeDetection for the affected grid, tree, hierarchical, and pivot suites.

  • Centralized async test helpers in helper-utils.spec.ts.

  • runAfterRenderOnce consumers now settle through shared helpers:

    • waitForGridSettle
    • setGridVerticalScrollTop
    • dispatchGridScrollEvents
    • navigateWithGridScroll
  • Updated the zoned originals to await the new render-boundary timing.

    • Previously, they happened to align with onStable.

Notes

  • Behavior for zoned apps is unchanged in practice.

    • Real interaction still triggers a render.
    • Only the timing signal moved from onStable to afterNextRender.
  • No runOutsideAngular was removed.

  • No forced detectChanges was added in zoneless tests.

Motivation / Context

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

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

This PR fixes multiple Grid (and related) features that previously relied on NgZone.onStable—which does not emit under Angular’s zoneless change detection—by migrating deferred DOM-/render-dependent work to Angular render hooks and aligning tests with the new render-boundary timing.

Changes:

  • Added runAfterRenderOnce(injector, cb, phase?) and migrated onStable-scheduled work across grids, virtualization, and related infrastructure to render hooks.
  • Updated Grid internals for zoneless correctness (e.g., virtualization scroll pipelines, autosizing, resizing notifications, keyboard navigation scheduling).
  • Added/updated zoneless test coverage using provideZonelessChangeDetection() plus shared async helpers for scroll/navigation settling.

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
projects/igniteui-angular/core/src/core/utils.ts Adds central runAfterRenderOnce helper built on afterNextRender.
projects/igniteui-angular/grids/grid/src/grid-base.directive.ts Replaces zone.onStable scheduling with runAfterRenderOnce and adjusts navigation callback guards.
projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts Migrates dimension autosize scheduling off onStable onto render hooks.
projects/igniteui-angular/grids/core/src/resizing/resizing.service.ts Ensures column resize notifies the grid in zoneless via render-bound callback.
projects/igniteui-angular/grids/core/src/grid-navigation.service.ts Broadens virtualized keyboard-selection notifications to any key that scrolls.
projects/igniteui-angular/grids/core/src/filtering/grid-filtering.service.ts Makes isFilterRowVisible signal-backed to re-render OnPush views in zoneless.
projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-cell.component.ts Adds resize observation so filter-chip visibility recalculates under zoneless.
projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts Migrates virtualization post-scroll work to render hooks; fixes reduce-on-empty edge case.
projects/igniteui-angular/test-utils/helper-utils.spec.ts Adds shared async/settling helpers for scroll/navigation and zoneless scroll detection.
projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.spec.ts Adds zoneless coverage for virtualization size recalculation after scroll.
projects/igniteui-angular/grids/grid/src/grid.search.spec.ts Updates timing around grouping + scroll highlight navigation; adds zoneless duplicate.
projects/igniteui-angular/grids/grid/src/grid.master-detail.spec.ts Updates navigation timing and adds zoneless navigation coverage for master-detail scenarios.
projects/igniteui-angular/grids/grid/src/grid.groupby.spec.ts Adds zoneless regression for horizontal virtualization state restore with cached row views.
projects/igniteui-angular/grids/grid/src/grid.component.spec.ts Replaces fixed delays with settle helpers; adds zoneless ARIA attribute regression.
projects/igniteui-angular/grids/grid/src/grid-mrl-keyboard-nav.spec.ts Replaces fixed delays with navigation+scroll settling helpers; adds zoneless coverage.
projects/igniteui-angular/grids/grid/src/grid-keyBoardNav.spec.ts Documents/adjusts zoned timing; adds zoneless virtualized navigation coverage.
projects/igniteui-angular/grids/grid/src/grid-keyBoardNav-headers.spec.ts Updates header navigation timing to await render-hook-driven state restoration; adds zoneless coverage.
projects/igniteui-angular/grids/grid/src/grid-filtering-ui.spec.ts Adds zoneless filtering-row UI coverage and adjusts resize-driven indicator assertions.
projects/igniteui-angular/grids/grid/src/grid-cell-selection.spec.ts Replaces fixed delays with settle helper; adds zoneless selection regression.
projects/igniteui-angular/grids/grid/src/column.spec.ts Adds zoneless coverage for date/time editor formatting behaviors.
projects/igniteui-angular/grids/grid/src/column-group.spec.ts Stabilizes width assertions (pixel tolerance) and adds zoneless duplicate coverage.
projects/igniteui-angular/grids/tree-grid/src/tree-grid.component.spec.ts Updates auto-generate timing and adds zoneless auto-generate coverage.
projects/igniteui-angular/grids/tree-grid/src/tree-grid-summaries.spec.ts Adds zoneless runtime summary settings coverage and stabilizes async waits.
projects/igniteui-angular/grids/tree-grid/src/tree-grid-keyBoardNav.spec.ts Stabilizes scroll-driven keyboard navigation via shared scroll helpers; adds zoneless suite.
projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.virtualization.spec.ts Uses shared scroll-top helper; adds zoneless virtualization regressions and increases timeout budget.
projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid.navigation.spec.ts Stabilizes scroll/navigation timing and adds zoneless navigation coverage with init/scroll settling helpers.
projects/igniteui-angular/grids/hierarchical-grid/src/hierarchical-grid-navigation.service.ts Subscribes to chunkLoad before scrolling to avoid missed synchronous emissions.
projects/igniteui-angular/grids/pivot-grid/src/pivot-grid-keyboard-nav.spec.ts Uses settle helper for horizontal virtualization navigation instead of fixed delays.
projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.spec.ts Replaces generated expression tree with explicit FilteringExpressionsTree initialization.
projects/igniteui-angular-elements/src/app/custom-strategy.spec.ts Replaces timer-based waits with childrenResolved event synchronization.

import { TestBed, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component, ViewChild } from '@angular/core';
import { Component, ViewChild, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component, ViewChild, DebugElement} from '@angular/core';
import { Component, ViewChild, DebugElement, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@@ -1,15 +1,16 @@
import { Component, ViewChild, OnInit, DebugElement, QueryList, TemplateRef, ViewChildren } from '@angular/core';
import { Component, ViewChild, OnInit, DebugElement, QueryList, TemplateRef, ViewChildren, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@@ -1,4 +1,4 @@
import { Component, ViewChild, TemplateRef, QueryList } from '@angular/core';
import { Component, ViewChild, TemplateRef, QueryList, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@@ -1,4 +1,4 @@
import { AfterViewInit, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild, TemplateRef, inject } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild, TemplateRef, inject, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@@ -1,4 +1,4 @@
import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core';
import { Component, DebugElement, TemplateRef, ViewChild, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@@ -1,5 +1,5 @@
import { AsyncPipe, NgClass, NgForOfContext } from '@angular/common';
import { AfterViewInit, ChangeDetectorRef, Component, Directive, Injectable, IterableDiffers, NgZone, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, DebugElement, Pipe, PipeTransform, inject } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, Directive, Injectable, IterableDiffers, NgZone, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, DebugElement, Pipe, PipeTransform, inject, ChangeDetectionStrategy, provideZonelessChangeDetection } from '@angular/core';
@viktorkombov viktorkombov added ❌ do not merge and removed ❌ status: awaiting-test PRs awaiting manual verification labels Jul 6, 2026
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.

2 participants