Skip to content

Commit

Permalink
fix(module:typography): focus the element and set the value even if t…
Browse files Browse the repository at this point in the history
…he zone is already stable
  • Loading branch information
arturovt committed Mar 22, 2022
1 parent 2d77bc2 commit 77ca222
Showing 1 changed file with 31 additions and 30 deletions.
61 changes: 31 additions & 30 deletions components/typography/text-edit.component.ts
Expand Up @@ -17,8 +17,8 @@ import {
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { BehaviorSubject, fromEvent, Observable } from 'rxjs';
import { filter, switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';
import { BehaviorSubject, EMPTY, from, fromEvent, Observable } from 'rxjs';
import { switchMap, take, takeUntil, withLatestFrom } from 'rxjs/operators';

import { NzDestroyService } from 'ng-zorro-antd/core/services';
import { NzTSType } from 'ng-zorro-antd/core/types';
Expand Down Expand Up @@ -66,9 +66,7 @@ export class NzTextEditComponent implements OnInit {
@Output() readonly endEditing = new EventEmitter<string>(true);
@ViewChild('textarea', { static: false })
set textarea(textarea: ElementRef<HTMLTextAreaElement> | undefined) {
if (textarea) {
this.textarea$.next(textarea);
}
this.textarea$.next(textarea);
}
@ViewChild(NzAutosizeDirective, { static: false }) autosizeDirective!: NzAutosizeDirective;

Expand All @@ -79,7 +77,7 @@ export class NzTextEditComponent implements OnInit {
// We could've saved the textarea within some private property (e.g. `_textarea`) and have a getter,
// but having subject makes the code more reactive and cancellable (e.g. event listeners will be
// automatically removed and re-added through the `switchMap` below).
private textarea$ = new BehaviorSubject<ElementRef<HTMLTextAreaElement> | null>(null);
private textarea$ = new BehaviorSubject<ElementRef<HTMLTextAreaElement> | null | undefined>(null);

constructor(
private ngZone: NgZone,
Expand All @@ -95,22 +93,19 @@ export class NzTextEditComponent implements OnInit {
this.cdr.markForCheck();
});

const textarea$: Observable<ElementRef<HTMLTextAreaElement>> = this.textarea$.pipe(
filter((textarea): textarea is ElementRef<HTMLTextAreaElement> => textarea !== null)
);

textarea$
this.textarea$
.pipe(
switchMap(
textarea =>
// Caretaker note: we explicitly should call `subscribe()` within the root zone.
// `runOutsideAngular(() => fromEvent(...))` will just create an observable within the root zone,
// but `addEventListener` is called when the `fromEvent` is subscribed.
new Observable<KeyboardEvent>(subscriber =>
this.ngZone.runOutsideAngular(() =>
fromEvent<KeyboardEvent>(textarea.nativeElement, 'keydown').subscribe(subscriber)
switchMap(textarea =>
// Caretaker note: we explicitly should call `subscribe()` within the root zone.
// `runOutsideAngular(() => fromEvent(...))` will just create an observable within the root zone,
// but `addEventListener` is called when the `fromEvent` is subscribed.
textarea
? new Observable<KeyboardEvent>(subscriber =>
this.ngZone.runOutsideAngular(() =>
fromEvent<KeyboardEvent>(textarea.nativeElement, 'keydown').subscribe(subscriber)
)
)
)
: EMPTY
),
takeUntil(this.destroy$)
)
Expand All @@ -133,15 +128,16 @@ export class NzTextEditComponent implements OnInit {
});
});

textarea$
this.textarea$
.pipe(
switchMap(
textarea =>
new Observable<KeyboardEvent>(subscriber =>
this.ngZone.runOutsideAngular(() =>
fromEvent<KeyboardEvent>(textarea.nativeElement, 'input').subscribe(subscriber)
switchMap(textarea =>
textarea
? new Observable<KeyboardEvent>(subscriber =>
this.ngZone.runOutsideAngular(() =>
fromEvent<KeyboardEvent>(textarea.nativeElement, 'input').subscribe(subscriber)
)
)
)
: EMPTY
),
takeUntil(this.destroy$)
)
Expand Down Expand Up @@ -175,15 +171,20 @@ export class NzTextEditComponent implements OnInit {
}

focusAndSetValue(): void {
this.ngZone.onStable
.pipe(take(1), withLatestFrom(this.textarea$), takeUntil(this.destroy$))
.subscribe(([, textarea]) => {
// 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();
}
});
});
}
}

0 comments on commit 77ca222

Please sign in to comment.