Skip to content

Commit 73af728

Browse files
perf(module:mention): do not trigger change detections if there are no event listeners (#7130)
* perf(module:mention): do not trigger change detections if there are no event listeners * fix(module:mention): fix wrong event listener Co-authored-by: simplejason <simplejason.coder@gmail.com>
1 parent eb3b1ba commit 73af728

1 file changed

Lines changed: 48 additions & 15 deletions

File tree

components/mention/mention-trigger.ts

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@
55

66
import {
77
AfterViewInit,
8+
ChangeDetectorRef,
89
Directive,
910
ElementRef,
1011
EventEmitter,
1112
ExistingProvider,
1213
forwardRef,
13-
OnDestroy
14+
NgZone,
15+
OnDestroy,
16+
Output
1417
} from '@angular/core';
1518
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
19+
import { fromEvent } from 'rxjs';
20+
import { takeUntil } from 'rxjs/operators';
1621

22+
import { NzDestroyService } from 'ng-zorro-antd/core/services';
1723
import { OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types';
1824

1925
import { NZ_MENTION_CONFIG } from './config';
@@ -29,28 +35,34 @@ export const NZ_MENTION_TRIGGER_ACCESSOR: ExistingProvider = {
2935
@Directive({
3036
selector: 'input[nzMentionTrigger], textarea[nzMentionTrigger]',
3137
exportAs: 'nzMentionTrigger',
32-
providers: [NZ_MENTION_TRIGGER_ACCESSOR],
38+
providers: [NzDestroyService, NZ_MENTION_TRIGGER_ACCESSOR],
3339
host: {
34-
autocomplete: 'off',
35-
'(focusin)': 'onFocusin.emit()',
36-
'(blur)': 'onBlur.emit()',
37-
'(input)': 'onInput.emit($event)',
38-
'(keydown)': 'onKeydown.emit($event)',
39-
'(click)': 'onClick.emit($event)'
40+
autocomplete: 'off'
4041
}
4142
})
4243
export class NzMentionTriggerDirective implements ControlValueAccessor, OnDestroy, AfterViewInit {
4344
onChange: OnChangeType = () => {};
4445
onTouched: OnTouchedType = () => {};
4546

46-
readonly onFocusin: EventEmitter<void> = new EventEmitter();
47-
readonly onBlur: EventEmitter<void> = new EventEmitter();
48-
readonly onInput: EventEmitter<KeyboardEvent> = new EventEmitter();
49-
readonly onKeydown: EventEmitter<KeyboardEvent> = new EventEmitter();
50-
readonly onClick: EventEmitter<MouseEvent> = new EventEmitter();
47+
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
48+
@Output() readonly onFocusin: EventEmitter<void> = new EventEmitter();
49+
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
50+
@Output() readonly onBlur: EventEmitter<void> = new EventEmitter();
51+
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
52+
@Output() readonly onInput: EventEmitter<KeyboardEvent> = new EventEmitter();
53+
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
54+
@Output() readonly onKeydown: EventEmitter<KeyboardEvent> = new EventEmitter();
55+
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
56+
@Output() readonly onClick: EventEmitter<MouseEvent> = new EventEmitter();
5157
value?: string;
5258

53-
constructor(public el: ElementRef, private nzMentionService: NzMentionService) {}
59+
constructor(
60+
public el: ElementRef<HTMLInputElement | HTMLTextAreaElement>,
61+
private ngZone: NgZone,
62+
private ref: ChangeDetectorRef,
63+
private destroy$: NzDestroyService,
64+
private nzMentionService: NzMentionService
65+
) {}
5466

5567
completeEvents(): void {
5668
this.onFocusin.complete();
@@ -60,7 +72,7 @@ export class NzMentionTriggerDirective implements ControlValueAccessor, OnDestro
6072
this.onClick.complete();
6173
}
6274

63-
focus(caretPos?: number): void {
75+
focus(caretPos: number | null = null): void {
6476
this.el.nativeElement.focus();
6577
this.el.nativeElement.setSelectionRange(caretPos, caretPos);
6678
}
@@ -98,9 +110,30 @@ export class NzMentionTriggerDirective implements ControlValueAccessor, OnDestro
98110

99111
ngAfterViewInit(): void {
100112
this.nzMentionService.registerTrigger(this);
113+
114+
this.setupEventListener('blur', this.onBlur);
115+
this.setupEventListener('focusin', this.onFocusin);
116+
this.setupEventListener('input', this.onInput, true);
117+
this.setupEventListener('click', this.onClick, true);
118+
this.setupEventListener('keydown', this.onKeydown, true);
101119
}
102120

103121
ngOnDestroy(): void {
104122
this.completeEvents();
105123
}
124+
125+
private setupEventListener<T>(eventName: string, eventEmitter: EventEmitter<T>, shouldPassEvent = false): void {
126+
this.ngZone.runOutsideAngular(() => {
127+
fromEvent<T>(this.el.nativeElement, eventName)
128+
.pipe(takeUntil(this.destroy$))
129+
.subscribe(event => {
130+
if (eventEmitter.observers.length) {
131+
this.ngZone.run(() => {
132+
eventEmitter.emit(shouldPassEvent ? event : undefined);
133+
this.ref.markForCheck();
134+
});
135+
}
136+
});
137+
});
138+
}
106139
}

0 commit comments

Comments
 (0)