diff --git a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.spec.ts b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.spec.ts index 5b0e44db0cd..beaa8480d19 100644 --- a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.spec.ts +++ b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.spec.ts @@ -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, provideZonelessChangeDetection } from '@angular/core'; import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -1230,6 +1230,45 @@ describe('IgxForOf directive -', () => { }); }); + describe('zoneless', () => { + let fix: ComponentFixture; + + beforeEach(async () => { + TestBed.resetTestingModule(); + await TestBed.configureTestingModule({ + imports: [VirtualComponent], + providers: [provideZonelessChangeDetection()] + }).compileComponents(); + + fix = TestBed.createComponent(VirtualComponent); + dg.generateData(300, 5, fix.componentInstance); + fix.detectChanges(); + }); + + it('should call recalcUpdateSizes after vertical scroll', async () => { + const virtDir = fix.componentInstance.parentVirtDir; + const spy = spyOn(virtDir, 'recalcUpdateSizes').and.callThrough(); + + fix.componentInstance.scrollTop(300); + await wait(100); + fix.detectChanges(); + + expect(spy).toHaveBeenCalled(); + }); + + it('should call recalcUpdateSizes after horizontal scroll', async () => { + const childDirs = fix.componentInstance.childVirtDirs.toArray(); + const spy = spyOn(childDirs[0], 'recalcUpdateSizes').and.callThrough(); + + fix.componentInstance.scrollLeft(500); + await wait(100); + fix.detectChanges(); + + expect(spy).toHaveBeenCalled(); + }); + + }); + describe('on create new instance', () => { let fix: ComponentFixture; diff --git a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts index c90cb47014e..cf52d57a1be 100644 --- a/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts @@ -857,6 +857,10 @@ export class IgxForOfDirective extends IgxForOfToken extends IgxForOfToken { afterNextRender({ write: () => { this.dc.instance._viewContainer.element.nativeElement.style.transform = `translateY(${-scrollOffset}px)`; - } + }, + mixedReadWrite: isZoneless ? () => { + this.recalcUpdateSizes(); + } : undefined }); }); - - this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this)); + if (!isZoneless) { + this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this)); + } this.dc.changeDetectorRef.detectChanges(); if (prevStartIndex !== this.state.startIndex) { @@ -1175,7 +1184,18 @@ export class IgxForOfDirective extends IgxForOfToken { + afterNextRender({ + mixedReadWrite: () => { + this.recalcUpdateSizes(); + } + }); + }); + } else { + this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this)); + } this.dc.changeDetectorRef.detectChanges(); if (prevStartIndex !== this.state.startIndex) { @@ -1777,12 +1797,18 @@ export class IgxGridForOfDirective extends IgxForOfDirec } const prevState = Object.assign({}, this.state); const scrollOffset = this.fixedUpdateAllElements(this._virtScrollPosition); + const isZoneless = this.isZonelessChangeDetection(); runInInjectionContext(this._injector, () => { afterNextRender({ write: () => { this.dc.instance._viewContainer.element.nativeElement.style.transform = `translateY(${-scrollOffset}px)`; - this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this, prevState)); - } + if (!isZoneless) { + this._zone.onStable.pipe(first()).subscribe(this.recalcUpdateSizes.bind(this, prevState)); + } + }, + mixedReadWrite: isZoneless ? () => { + this.recalcUpdateSizes(prevState); + } : undefined }); }); diff --git a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts index 5562b556958..53bd7832fb0 100644 --- a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts +++ b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts @@ -3733,7 +3733,7 @@ export abstract class IgxGridBaseDirective implements GridType, destructor ) .subscribe(() => { - this.zone.run(() => { + const work = () => { // do not trigger reflow if element is detached. if (this.nativeElement.isConnected) { if (this.shouldResize) { @@ -3748,7 +3748,12 @@ export abstract class IgxGridBaseDirective implements GridType, } this.notifyChanges(true); } - }); + }; + if (this.isZonelessChangeDetection()) { + work(); + } else { + this.zone.run(work); + } }); this.pipeTriggerNotifier.pipe(takeUntil(this.destroy$)).subscribe(() => this.pipeTrigger++);