@@ -58,7 +54,8 @@ 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;
@@ -66,7 +63,6 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
static ngAcceptInputType_nzDuration: NumberInput;
private scrollListenerDestroy$ = new Subject();
- private destroy$ = new Subject();
private target: HTMLElement | null = null;
visible: boolean = false;
@@ -78,6 +74,26 @@ export class NzBackTopComponent implements OnInit, OnDestroy, OnChanges {
@Input() @InputNumber() nzDuration: number = 450;
@Output() readonly nzClick: EventEmitter = new EventEmitter();
+ @ViewChild('backTop', { static: false })
+ set backTop(backTop: ElementRef | 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,
@@ -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;
@@ -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;
}
@@ -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 {
diff --git a/components/back-top/back-top.spec.ts b/components/back-top/back-top.spec.ts
index 85a8ed6696..798cd76096 100644
--- a/components/back-top/back-top.spec.ts
+++ b/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';
@@ -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]', () => {