Skip to content

Commit cbfc558

Browse files
authored
perf(module:steps): do not run change detection if there are no nzIndexChange listeners (#7183)
1 parent 2a93d05 commit cbfc558

2 files changed

Lines changed: 45 additions & 37 deletions

File tree

components/steps/step.component.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import {
77
ChangeDetectionStrategy,
88
ChangeDetectorRef,
99
Component,
10+
ElementRef,
1011
Input,
11-
OnDestroy,
12+
NgZone,
13+
OnInit,
1214
TemplateRef,
1315
ViewChild,
1416
ViewEncapsulation
1517
} from '@angular/core';
16-
import { Subject } from 'rxjs';
18+
import { fromEvent, Subject } from 'rxjs';
19+
import { filter, takeUntil } from 'rxjs/operators';
1720

21+
import { NzDestroyService } from 'ng-zorro-antd/core/services';
1822
import { BooleanInput, NgClassType } from 'ng-zorro-antd/core/types';
1923
import { InputBoolean } from 'ng-zorro-antd/core/util';
2024
import { NzProgressFormatter } from 'ng-zorro-antd/progress';
@@ -27,10 +31,10 @@ import { NzProgressFormatter } from 'ng-zorro-antd/progress';
2731
preserveWhitespaces: false,
2832
template: `
2933
<div
34+
#itemContainer
3035
class="ant-steps-item-container"
3136
[attr.role]="clickable && !nzDisabled ? 'button' : null"
3237
[tabindex]="clickable && !nzDisabled ? 0 : null"
33-
(click)="onClick()"
3438
>
3539
<div class="ant-steps-item-tail" *ngIf="last !== true"></div>
3640
<div class="ant-steps-item-icon">
@@ -94,12 +98,14 @@ import { NzProgressFormatter } from 'ng-zorro-antd/progress';
9498
'[class.ant-steps-item-disabled]': 'nzDisabled',
9599
'[class.ant-steps-item-custom]': '!!nzIcon',
96100
'[class.ant-steps-next-error]': '(outStatus === "error") && (currentIndex === index + 1)'
97-
}
101+
},
102+
providers: [NzDestroyService]
98103
})
99-
export class NzStepComponent implements OnDestroy {
104+
export class NzStepComponent implements OnInit {
100105
static ngAcceptInputType_nzDisabled: BooleanInput;
101106

102107
@ViewChild('processDotTemplate', { static: false }) processDotTemplate?: TemplateRef<void>;
108+
@ViewChild('itemContainer', { static: true }) itemContainer!: ElementRef<HTMLElement>;
103109

104110
@Input() nzTitle?: string | TemplateRef<void>;
105111
@Input() nzSubtitle?: string | TemplateRef<void>;
@@ -143,7 +149,8 @@ export class NzStepComponent implements OnDestroy {
143149
outStatus = 'process';
144150
showProcessDot = false;
145151
clickable = false;
146-
click$ = new Subject<number>();
152+
153+
clickOutsideAngular$ = new Subject<number>();
147154

148155
readonly nullProcessFormat: NzProgressFormatter = () => null;
149156

@@ -170,12 +177,19 @@ export class NzStepComponent implements OnDestroy {
170177

171178
private _currentIndex = 0;
172179

173-
constructor(private cdr: ChangeDetectorRef) {}
174-
175-
onClick(): void {
176-
if (this.clickable && this.currentIndex !== this.index && !this.nzDisabled) {
177-
this.click$.next(this.index);
178-
}
180+
constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone, private destroy$: NzDestroyService) {}
181+
182+
ngOnInit(): void {
183+
this.ngZone.runOutsideAngular(() =>
184+
fromEvent(this.itemContainer.nativeElement, 'click')
185+
.pipe(
186+
filter(() => this.clickable && this.currentIndex !== this.index && !this.nzDisabled),
187+
takeUntil(this.destroy$)
188+
)
189+
.subscribe(() => {
190+
this.clickOutsideAngular$.next(this.index);
191+
})
192+
);
179193
}
180194

181195
enable(): void {
@@ -191,8 +205,4 @@ export class NzStepComponent implements OnDestroy {
191205
markForCheck(): void {
192206
this.cdr.markForCheck();
193207
}
194-
195-
ngOnDestroy(): void {
196-
this.click$.complete();
197-
}
198208
}

components/steps/steps.component.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
ElementRef,
1414
EventEmitter,
1515
Input,
16+
NgZone,
1617
OnChanges,
17-
OnDestroy,
1818
OnInit,
1919
Optional,
2020
Output,
@@ -24,9 +24,10 @@ import {
2424
TemplateRef,
2525
ViewEncapsulation
2626
} from '@angular/core';
27-
import { merge, Subject, Subscription } from 'rxjs';
27+
import { merge, Subscription } from 'rxjs';
2828
import { startWith, takeUntil } from 'rxjs/operators';
2929

30+
import { NzDestroyService } from 'ng-zorro-antd/core/services';
3031
import { BooleanInput, NgClassType, NzSizeDSType } from 'ng-zorro-antd/core/types';
3132
import { toBoolean } from 'ng-zorro-antd/core/util';
3233

@@ -35,6 +36,7 @@ import { NzStepComponent } from './step.component';
3536
export type NzDirectionType = 'horizontal' | 'vertical';
3637
export type NzStatusType = 'wait' | 'process' | 'finish' | 'error';
3738
export type nzProgressDotTemplate = TemplateRef<{ $implicit: TemplateRef<void>; status: string; index: number }>;
39+
3840
@Component({
3941
changeDetection: ChangeDetectionStrategy.OnPush,
4042
encapsulation: ViewEncapsulation.None,
@@ -45,9 +47,10 @@ export type nzProgressDotTemplate = TemplateRef<{ $implicit: TemplateRef<void>;
4547
<div class="ant-steps" [ngClass]="classMap">
4648
<ng-content></ng-content>
4749
</div>
48-
`
50+
`,
51+
providers: [NzDestroyService]
4952
})
50-
export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterContentInit {
53+
export class NzStepsComponent implements OnChanges, OnInit, AfterContentInit {
5154
static ngAcceptInputType_nzProgressDot: BooleanInput | nzProgressDotTemplate | undefined | null;
5255

5356
@ContentChildren(NzStepComponent) steps!: QueryList<NzStepComponent>;
@@ -73,19 +76,20 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
7376

7477
@Output() readonly nzIndexChange = new EventEmitter<number>();
7578

76-
private destroy$ = new Subject<void>();
77-
private indexChangeSubscription?: Subscription;
79+
private indexChangeSubscription = Subscription.EMPTY;
7880

7981
showProcessDot = false;
8082
customProcessDotTemplate?: TemplateRef<{ $implicit: TemplateRef<void>; status: string; index: number }>;
8183
classMap: NgClassType = {};
8284
dir: Direction = 'ltr';
8385

8486
constructor(
87+
private ngZone: NgZone,
8588
private elementRef: ElementRef,
8689
private renderer: Renderer2,
8790
private cdr: ChangeDetectorRef,
88-
@Optional() private directionality: Directionality
91+
@Optional() private directionality: Directionality,
92+
private destroy$: NzDestroyService
8993
) {
9094
this.setClassMap();
9195
}
@@ -111,14 +115,6 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
111115
this.updateChildrenSteps();
112116
}
113117

114-
ngOnDestroy(): void {
115-
this.destroy$.next();
116-
this.destroy$.complete();
117-
if (this.indexChangeSubscription) {
118-
this.indexChangeSubscription.unsubscribe();
119-
}
120-
}
121-
122118
ngAfterContentInit(): void {
123119
if (this.steps) {
124120
this.steps.changes.pipe(startWith(null), takeUntil(this.destroy$)).subscribe(() => {
@@ -159,12 +155,14 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont
159155
step.markForCheck();
160156
});
161157
});
162-
if (this.indexChangeSubscription) {
163-
this.indexChangeSubscription.unsubscribe();
164-
}
165-
this.indexChangeSubscription = merge(...this.steps.map(step => step.click$)).subscribe(index =>
166-
this.nzIndexChange.emit(index)
167-
);
158+
this.indexChangeSubscription.unsubscribe();
159+
this.indexChangeSubscription = merge(...this.steps.map(step => step.clickOutsideAngular$))
160+
.pipe(takeUntil(this.destroy$))
161+
.subscribe(index => {
162+
if (this.nzIndexChange.observers.length) {
163+
this.ngZone.run(() => this.nzIndexChange.emit(index));
164+
}
165+
});
168166
}
169167
}
170168

0 commit comments

Comments
 (0)