Skip to content

Commit

Permalink
fix(module:typography): remove NgZone dependency (#8440)
Browse files Browse the repository at this point in the history
This commit eliminates the `NgZone` dependency from the text edit 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 20, 2024
1 parent 1ec0e76 commit af7fb5d
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 28 deletions.
47 changes: 29 additions & 18 deletions components/typography/text-edit.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import {
Component,
ElementRef,
EventEmitter,
Injector,
Input,
NgZone,
OnInit,
Output,
ViewChild,
ViewEncapsulation
ViewEncapsulation,
afterNextRender,
inject
} from '@angular/core';
import { BehaviorSubject, EMPTY, from, fromEvent, Observable } from 'rxjs';
import { switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';
import { BehaviorSubject, EMPTY, fromEvent, Observable } from 'rxjs';
import { first, switchMap, takeUntil } from 'rxjs/operators';

import { NzOutletModule } from 'ng-zorro-antd/core/outlet';
import { NzDestroyService } from 'ng-zorro-antd/core/services';
Expand Down Expand Up @@ -86,6 +89,8 @@ export class NzTextEditComponent implements OnInit {
// automatically removed and re-added through the `switchMap` below).
private textarea$ = new BehaviorSubject<ElementRef<HTMLTextAreaElement> | null | undefined>(null);

private injector = inject(Injector);

constructor(
private ngZone: NgZone,
private host: ElementRef<HTMLElement>,
Expand Down Expand Up @@ -178,20 +183,26 @@ export class NzTextEditComponent implements OnInit {
}

focusAndSetValue(): void {
// Note: the zone may be nooped through `BootstrapOptions` when bootstrapping the root module. This means
// the `onStable` will never emit any value.
const onStable$ = this.ngZone.isStable ? from(Promise.resolve()) : this.ngZone.onStable.pipe(take(1));
// Normally this isn't in the zone, but it can cause performance regressions for apps
// using `zone-patch-rxjs` because it'll trigger a change detection when it unsubscribes.
this.ngZone.runOutsideAngular(() => {
onStable$.pipe(withLatestFrom(this.textarea$), takeUntil(this.destroy$)).subscribe(([, textarea]) => {
if (textarea) {
textarea.nativeElement.focus();
textarea.nativeElement.value = this.currentText || '';
this.autosizeDirective.resizeToFitContent();
this.cdr.markForCheck();
}
});
});
const { injector } = this;

afterNextRender(
() => {
this.textarea$
.pipe(
// It may still not be available, so we need to wait until view queries
// are executed during the change detection. It's safer to wait until
// the query runs and the textarea is set on the behavior subject.
first((textarea): textarea is ElementRef<HTMLTextAreaElement> => textarea != null),
takeUntil(this.destroy$)
)
.subscribe(textarea => {
textarea.nativeElement.focus();
textarea.nativeElement.value = this.currentText || '';
this.autosizeDirective.resizeToFitContent();
this.cdr.markForCheck();
});
},
{ injector }
);
}
}
16 changes: 6 additions & 10 deletions components/typography/typography.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CAPS_LOCK, ENTER, ESCAPE, TAB } from '@angular/cdk/keycodes';
import { OverlayContainer } from '@angular/cdk/overlay';
import { CommonModule } from '@angular/common';
import { ApplicationRef, Component, NgZone, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, flushMicrotasks, inject, TestBed, tick } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

Expand Down Expand Up @@ -279,22 +279,18 @@ describe('typography', () => {
expect(testComponent.str).toBe('test');
}));

it('should edit focus', fakeAsync(() => {
it('should edit focus', () => {
const editButton = componentElement.querySelector<HTMLButtonElement>('.ant-typography-edit');
editButton!.click();
fixture.detectChanges();
// The zone may be already stable (see `isStable` condition), thus there're no tasks
// in the queue that have been scheduled previously.
// This will schedule a microtask (except of waiting for `onStable`).
flushMicrotasks();

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

const textarea = componentElement.querySelector<HTMLTextAreaElement>('textarea')! as HTMLTextAreaElement;

expect(document.activeElement === textarea).toBe(true);
dispatchFakeEvent(textarea, 'blur');
flush();
fixture.detectChanges();
}));
});

it('should apply changes when Enter keydown', fakeAsync(() => {
const editButton = componentElement.querySelector<HTMLButtonElement>('.ant-typography-edit');
Expand Down

0 comments on commit af7fb5d

Please sign in to comment.