Skip to content

Commit

Permalink
perf(module:back-top): do not run change detection if there are no `n…
Browse files Browse the repository at this point in the history
…zClick` listeners (#7179)
  • Loading branch information
arturovt committed Feb 18, 2022
1 parent 0054f59 commit 7d091bb
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 19 deletions.
46 changes: 28 additions & 18 deletions components/back-top/back-top.component.ts
Expand Up @@ -10,6 +10,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Inject,
Input,
Expand All @@ -21,14 +22,15 @@ import {
Output,
SimpleChanges,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { fromEvent, Subject, Subscription } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';

import { fadeMotion } from 'ng-zorro-antd/core/animation';
import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';
import { NzScrollService } from 'ng-zorro-antd/core/services';
import { NzDestroyService, NzScrollService } from 'ng-zorro-antd/core/services';
import { NumberInput, NzSafeAny } from 'ng-zorro-antd/core/types';
import { InputNumber } from 'ng-zorro-antd/core/util';

Expand All @@ -39,13 +41,7 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'backTop';
exportAs: 'nzBackTop',
animations: [fadeMotion],
template: `
<div
class="ant-back-top"
[class.ant-back-top-rtl]="dir === 'rtl'"
(click)="clickBackTop()"
@fadeMotion
*ngIf="visible"
>
<div #backTop class="ant-back-top" [class.ant-back-top-rtl]="dir === 'rtl'" @fadeMotion *ngIf="visible">
<ng-template #defaultContent>
<div class="ant-back-top-content">
<div class="ant-back-top-icon">
Expand All @@ -58,15 +54,15 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'backTop';
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false
preserveWhitespaces: false,
providers: [NzDestroyService]
})
export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;
static ngAcceptInputType_nzVisibilityHeight: NumberInput;
static ngAcceptInputType_nzDuration: NumberInput;

private scrollListenerDestroy$ = new Subject();
private destroy$ = new Subject();
private target: HTMLElement | null = null;

visible: boolean = false;
Expand All @@ -78,6 +74,26 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
@Input() @InputNumber() nzDuration: number = 450;
@Output() readonly nzClick: EventEmitter<boolean> = new EventEmitter();

@ViewChild('backTop', { static: false })
set backTop(backTop: ElementRef<HTMLElement> | undefined) {
if (backTop) {
this.backTopClickSubscription.unsubscribe();

this.backTopClickSubscription = this.zone.runOutsideAngular(() =>
fromEvent(backTop.nativeElement, 'click')
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.scrollSrv.scrollTo(this.getTarget(), 0, { duration: this.nzDuration });
if (this.nzClick.observers.length) {
this.zone.run(() => this.nzClick.emit(true));
}
})
);
}
}

private backTopClickSubscription = Subscription.EMPTY;

constructor(
@Inject(DOCUMENT) private doc: NzSafeAny,
public nzConfigService: NzConfigService,
Expand All @@ -86,6 +102,7 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
private cd: ChangeDetectorRef,
private zone: NgZone,
private cdr: ChangeDetectorRef,
private destroy$: NzDestroyService,
@Optional() private directionality: Directionality
) {
this.dir = this.directionality.value;
Expand All @@ -102,11 +119,6 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
this.dir = this.directionality.value;
}

clickBackTop(): void {
this.scrollSrv.scrollTo(this.getTarget(), 0, { duration: this.nzDuration });
this.nzClick.emit(true);
}

private getTarget(): HTMLElement | Window {
return this.target || window;
}
Expand Down Expand Up @@ -135,8 +147,6 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
ngOnDestroy(): void {
this.scrollListenerDestroy$.next();
this.scrollListenerDestroy$.complete();
this.destroy$.next();
this.destroy$.complete();
}

ngOnChanges(changes: SimpleChanges): void {
Expand Down
18 changes: 17 additions & 1 deletion components/back-top/back-top.spec.ts
@@ -1,4 +1,4 @@
import { Component, DebugElement, ViewChild } from '@angular/core';
import { ApplicationRef, Component, DebugElement, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
Expand Down Expand Up @@ -143,6 +143,22 @@ describe('Component:nz-back-top', () => {
componentObject.clickBackTop();
}));
});

describe('change detection behavior', () => {
it('should not run change detection if there are no `nzClick` listeners', () => {
const appRef = TestBed.inject(ApplicationRef);
spyOn(appRef, 'tick');

const backTopButton = componentObject.backTopButton().nativeElement;
backTopButton.dispatchEvent(new MouseEvent('click'));
expect(appRef.tick).not.toHaveBeenCalled();

component.nzClick.subscribe();

backTopButton.dispatchEvent(new MouseEvent('click'));
expect(appRef.tick).toHaveBeenCalled();
});
});
});

describe('[nzTarget]', () => {
Expand Down

0 comments on commit 7d091bb

Please sign in to comment.