Skip to content

Commit

Permalink
perf(module:dropdown): do not run change detection if the dropdown ha…
Browse files Browse the repository at this point in the history
…s been clicked inside (#7135)
  • Loading branch information
arturovt committed Feb 25, 2022
1 parent fc991d1 commit 4679592
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 14 deletions.
18 changes: 17 additions & 1 deletion components/dropdown/context-menu.service.spec.ts
@@ -1,5 +1,5 @@
import { OverlayContainer, ScrollDispatcher } from '@angular/cdk/overlay';
import { Component, Provider, Type, ViewChild } from '@angular/core';
import { ApplicationRef, Component, Provider, Type, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Subject } from 'rxjs';
Expand Down Expand Up @@ -119,6 +119,22 @@ describe('context-menu', () => {
expect(overlayContainerElement.textContent).toBe('');
}).not.toThrowError();
}));
it('should not run change detection if the overlay is clicked inside', async () => {
const fixture = createComponent(NzTestDropdownContextMenuComponent, [], []);
fixture.detectChanges();
const fakeEvent = createMouseEvent('contextmenu', 300, 300);
const component = fixture.componentInstance;
component.nzContextMenuService.create(fakeEvent, component.nzDropdownMenuComponent);
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const appRef = TestBed.inject(ApplicationRef);
spyOn(appRef, 'tick');
overlayContainerElement.querySelector('ul')!.click();
expect(appRef.tick).toHaveBeenCalledTimes(0);
document.body.click();
expect(appRef.tick).toHaveBeenCalledTimes(1);
});
});

@Component({
Expand Down
33 changes: 20 additions & 13 deletions components/dropdown/context-menu.service.ts
Expand Up @@ -5,8 +5,8 @@

import { ConnectionPositionPair, Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { Injectable } from '@angular/core';
import { fromEvent, merge, Subscription } from 'rxjs';
import { Injectable, NgZone } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { filter, take } from 'rxjs/operators';

import { NzContextMenuServiceModule } from './context-menu.service.module';
Expand All @@ -26,7 +26,7 @@ export class NzContextMenuService {
private overlayRef: OverlayRef | null = null;
private closeSubscription = Subscription.EMPTY;

constructor(private overlay: Overlay) {}
constructor(private ngZone: NgZone, private overlay: Overlay) {}

create($event: MouseEvent | { x: number; y: number }, nzDropdownMenuComponent: NzDropdownMenuComponent): void {
this.close(true);
Expand All @@ -44,17 +44,24 @@ export class NzContextMenuService {
disposeOnNavigation: true,
scrollStrategy: this.overlay.scrollStrategies.close()
});
this.closeSubscription = merge(
nzDropdownMenuComponent.descendantMenuItemClick$,
fromEvent<MouseEvent>(document, 'click').pipe(
filter(event => !!this.overlayRef && !this.overlayRef.overlayElement.contains(event.target as HTMLElement)),
/** handle firefox contextmenu event **/
filter(event => event.button !== 2),
take(1)

this.closeSubscription = new Subscription();

this.closeSubscription.add(nzDropdownMenuComponent.descendantMenuItemClick$.subscribe(() => this.close()));

this.closeSubscription.add(
this.ngZone.runOutsideAngular(() =>
fromEvent<MouseEvent>(document, 'click')
.pipe(
filter(event => !!this.overlayRef && !this.overlayRef.overlayElement.contains(event.target as HTMLElement)),
/** handle firefox contextmenu event **/
filter(event => event.button !== 2),
take(1)
)
.subscribe(() => this.ngZone.run(() => this.close()))
)
).subscribe(() => {
this.close();
});
);

this.overlayRef.attach(
new TemplatePortal(nzDropdownMenuComponent.templateRef, nzDropdownMenuComponent.viewContainerRef)
);
Expand Down

0 comments on commit 4679592

Please sign in to comment.