Skip to content

Commit

Permalink
perf(module:mention): do not run change detections on mousedown eve…
Browse files Browse the repository at this point in the history
…nts (#7094)
  • Loading branch information
arturovt committed Dec 16, 2021
1 parent 346c50d commit 0d4ad23
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 4 deletions.
30 changes: 27 additions & 3 deletions components/mention/mention.component.ts
Expand Up @@ -15,6 +15,7 @@ import {
import { TemplatePortal } from '@angular/cdk/portal';
import { DOCUMENT } from '@angular/common';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Expand All @@ -23,6 +24,7 @@ import {
EventEmitter,
Inject,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
Expand All @@ -35,7 +37,8 @@ import {
ViewChildren,
ViewContainerRef
} from '@angular/core';
import { fromEvent, merge, Subscription } from 'rxjs';
import { fromEvent, merge, Observable, Subscription } from 'rxjs';
import { startWith, switchMap } from 'rxjs/operators';

import { DEFAULT_MENTION_BOTTOM_POSITIONS, DEFAULT_MENTION_TOP_POSITIONS } from 'ng-zorro-antd/core/overlay';
import { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types';
Expand Down Expand Up @@ -71,7 +74,6 @@ export type MentionPlacement = 'top' | 'bottom';
class="ant-mention-dropdown-item"
*ngFor="let suggestion of filteredSuggestions; let i = index"
[class.focus]="i === activeIndex"
(mousedown)="$event.preventDefault()"
(click)="selectSuggestion(suggestion)"
>
<ng-container *ngIf="suggestionTemplate; else defaultSuggestion">
Expand All @@ -90,7 +92,7 @@ export type MentionPlacement = 'top' | 'bottom';
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [NzMentionService]
})
export class NzMentionComponent implements OnDestroy, OnInit, OnChanges {
export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnChanges {
static ngAcceptInputType_nzLoading: BooleanInput;

@Input() nzValueWith: (value: NzSafeAny) => string = value => value;
Expand Down Expand Up @@ -141,6 +143,7 @@ export class NzMentionComponent implements OnDestroy, OnInit, OnChanges {
}

constructor(
private ngZone: NgZone,
@Optional() @Inject(DOCUMENT) private ngDocument: NzSafeAny,
private cdr: ChangeDetectorRef,
private overlay: Overlay,
Expand All @@ -167,6 +170,27 @@ export class NzMentionComponent implements OnDestroy, OnInit, OnChanges {
}
}

ngAfterViewInit(): void {
this.items.changes
.pipe(
startWith(this.items),
switchMap(() => {
const items = this.items.toArray();
// 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.
return new Observable<MouseEvent>(subscriber =>
this.ngZone.runOutsideAngular(() =>
merge(...items.map(item => fromEvent<MouseEvent>(item.nativeElement, 'mousedown'))).subscribe(subscriber)
)
);
})
)
.subscribe(event => {
event.preventDefault();
});
}

ngOnDestroy(): void {
this.closeDropdown();
}
Expand Down
22 changes: 21 additions & 1 deletion components/mention/nz-mention.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { Directionality } from '@angular/cdk/bidi';
import { DOWN_ARROW, ENTER, ESCAPE, RIGHT_ARROW, TAB, UP_ARROW } from '@angular/cdk/keycodes';
import { OverlayContainer } from '@angular/cdk/overlay';
import { ScrollDispatcher } from '@angular/cdk/scrolling';
import { Component, NgZone, ViewChild } from '@angular/core';
import { ApplicationRef, Component, NgZone, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
Expand Down Expand Up @@ -157,6 +157,26 @@ describe('mention', () => {
expect(textarea.value).toEqual('@angular ');
}));

it('should prevent default on the mousedown event when an option is clicked and should not run change detection', fakeAsync(() => {
textarea.value = '@a';
fixture.detectChanges();
dispatchFakeEvent(textarea, 'click');
fixture.detectChanges();
flush();

const appRef = TestBed.inject(ApplicationRef);
const option = overlayContainerElement.querySelector('.ant-mention-dropdown-item') as HTMLElement;
const event = new MouseEvent('mousedown');

spyOn(appRef, 'tick');
spyOn(event, 'preventDefault').and.callThrough();

option.dispatchEvent(event);

expect(event.preventDefault).toHaveBeenCalled();
expect(appRef.tick).not.toHaveBeenCalled();
}));

it('should support switch trigger', fakeAsync(() => {
fixture.componentInstance.inputTrigger = true;
fixture.detectChanges();
Expand Down

0 comments on commit 0d4ad23

Please sign in to comment.