Skip to content

Commit

Permalink
fix(module:autocomplete): remove NgZone dependency (#8462)
Browse files Browse the repository at this point in the history
This commit eliminates the `NgZone` dependency from the autocomplete component.
Since zone.js is becoming optional, `onStable` won't emit any value when `provideZonelessChangeDetection`
is used in the application config. Instead, we now use the alternative approach provided by `afterNextRender`.
Most of the code that was using `onStable` should be replaced with `afterNextRender`.
  • Loading branch information
arturovt committed Mar 25, 2024
1 parent 025df9c commit 24bb1bc
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 20 deletions.
28 changes: 14 additions & 14 deletions components/auto-complete/autocomplete.component.ts
Expand Up @@ -17,7 +17,6 @@ import {
EventEmitter,
Host,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
Expand All @@ -28,13 +27,15 @@ import {
TemplateRef,
ViewChild,
ViewChildren,
ViewEncapsulation
ViewEncapsulation,
inject
} from '@angular/core';
import { defer, merge, Observable, Subject, Subscription } from 'rxjs';
import { filter, switchMap, take, takeUntil } from 'rxjs/operators';
import { filter, switchMap, takeUntil } from 'rxjs/operators';

import { slideMotion } from 'ng-zorro-antd/core/animation';
import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation';
import { NZ_AFTER_NEXT_RENDER$ } from 'ng-zorro-antd/core/render';
import { BooleanInput, CompareWith, NzSafeAny } from 'ng-zorro-antd/core/types';
import { InputBoolean } from 'ng-zorro-antd/core/util';

Expand Down Expand Up @@ -149,29 +150,28 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit,
private selectionChangeSubscription: Subscription | null = Subscription.EMPTY;
private optionMouseEnterSubscription: Subscription | null = Subscription.EMPTY;
private dataSourceChangeSubscription: Subscription | null = Subscription.EMPTY;

/** Options changes listener */
readonly optionSelectionChanges: Observable<NzOptionSelectionChange> = defer(() => {
private readonly optionSelectionChanges: Observable<NzOptionSelectionChange> = defer(() => {
if (this.options) {
return merge<NzOptionSelectionChange[]>(...this.options.map(option => option.selectionChange));
}
return this.ngZone.onStable.asObservable().pipe(
take(1),
switchMap(() => this.optionSelectionChanges)
);

return this.afterNextRender$.pipe(switchMap(() => this.optionSelectionChanges));
});
readonly optionMouseEnter: Observable<NzAutocompleteOptionComponent> = defer(() => {

private readonly optionMouseEnter: Observable<NzAutocompleteOptionComponent> = defer(() => {
if (this.options) {
return merge<NzAutocompleteOptionComponent[]>(...this.options.map(option => option.mouseEntered));
}
return this.ngZone.onStable.asObservable().pipe(
take(1),
switchMap(() => this.optionMouseEnter)
);

return this.afterNextRender$.pipe(switchMap(() => this.optionMouseEnter));
});

private afterNextRender$ = inject(NZ_AFTER_NEXT_RENDER$);

constructor(
private changeDetectorRef: ChangeDetectorRef,
private ngZone: NgZone,
@Optional() private directionality: Directionality,
@Host() @Optional() public noAnimation?: NzNoAnimationDirective
) {}
Expand Down
12 changes: 6 additions & 6 deletions components/auto-complete/autocomplete.spec.ts
Expand Up @@ -630,9 +630,9 @@ describe('auto-complete', () => {

let options = overlayContainerElement.querySelectorAll('nz-auto-option') as NodeListOf<HTMLElement>;
options[0].click();
fixture.detectChanges();
zone.simulateZoneExit();
fixture.detectChanges();

// `tick()` will handle over after next render hooks.
TestBed.inject(ApplicationRef).tick();

const componentOptions = fixture.componentInstance.optionComponents.toArray();
expect(componentOptions[0].selected).toBe(true);
Expand All @@ -655,9 +655,9 @@ describe('auto-complete', () => {

let options = overlayContainerElement.querySelectorAll('nz-auto-option') as NodeListOf<HTMLElement>;
options[0].click();
fixture.detectChanges();
zone.simulateZoneExit();
fixture.detectChanges();

// `tick()` will handle over after next render hooks.
TestBed.inject(ApplicationRef).tick();

const componentOptions = fixture.componentInstance.optionComponents.toArray();
expect(componentOptions[0].selected).toBe(true);
Expand Down
32 changes: 32 additions & 0 deletions components/core/render/after-next-render.ts
@@ -0,0 +1,32 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { InjectionToken, Injector, afterNextRender, inject } from '@angular/core';
import { Observable } from 'rxjs';

/**
* An injection token representing `afterNextRender` as an observable rather
* than a callback-based API has been added. This might be necessary in code
* where streams of data are already being used and we need to wait until
* the change detection ends before performing any tasks.
*/
export const NZ_AFTER_NEXT_RENDER$ = new InjectionToken<Observable<void>>('nz-after-next-render', {
providedIn: 'root',
factory: () => {
const injector = inject(Injector);

return new Observable<void>(subscriber => {
const ref = afterNextRender(
() => {
subscriber.next();
subscriber.complete();
},
{ injector }
);

return () => ref.destroy();
});
}
});
6 changes: 6 additions & 0 deletions components/core/render/index.ts
@@ -0,0 +1,6 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export * from './public-api';
5 changes: 5 additions & 0 deletions components/core/render/ng-package.json
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "public-api.ts"
}
}
6 changes: 6 additions & 0 deletions components/core/render/public-api.ts
@@ -0,0 +1,6 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export * from './after-next-render';

0 comments on commit 24bb1bc

Please sign in to comment.