Skip to content

Commit 9971faa

Browse files
authored
perf(module:input-number): reduce change detection cycles (#7129)
1 parent 1fec1e0 commit 9971faa

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

components/input-number/input-number.component.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
EventEmitter,
1616
forwardRef,
1717
Input,
18+
NgZone,
1819
OnChanges,
1920
OnDestroy,
2021
OnInit,
@@ -25,7 +26,7 @@ import {
2526
ViewEncapsulation
2627
} from '@angular/core';
2728
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
28-
import { Subject } from 'rxjs';
29+
import { fromEvent, Subject } from 'rxjs';
2930
import { takeUntil } from 'rxjs/operators';
3031

3132
import { BooleanInput, NzSizeLDSType, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types';
@@ -70,8 +71,6 @@ import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util';
7071
[placeholder]="nzPlaceHolder"
7172
[attr.step]="nzStep"
7273
[attr.inputmode]="nzInputMode"
73-
(keydown)="onKeyDown($event)"
74-
(keyup)="stop()"
7574
[ngModel]="displayValue"
7675
(ngModelChange)="onModelChange($event)"
7776
/>
@@ -336,20 +335,6 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
336335
this.inputElement.nativeElement.value = `${displayValue}`;
337336
}
338337

339-
onKeyDown(e: KeyboardEvent): void {
340-
if (e.keyCode === UP_ARROW) {
341-
const ratio = this.getRatio(e);
342-
this.up(e, ratio);
343-
this.stop();
344-
} else if (e.keyCode === DOWN_ARROW) {
345-
const ratio = this.getRatio(e);
346-
this.down(e, ratio);
347-
this.stop();
348-
} else if (e.keyCode === ENTER) {
349-
this.updateDisplayValue(this.value!);
350-
}
351-
}
352-
353338
writeValue(value: number): void {
354339
this.value = value;
355340
this.setValue(value);
@@ -379,7 +364,8 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
379364
}
380365

381366
constructor(
382-
private elementRef: ElementRef,
367+
private ngZone: NgZone,
368+
private elementRef: ElementRef<HTMLElement>,
383369
private cdr: ChangeDetectorRef,
384370
private focusMonitor: FocusMonitor,
385371
@Optional() private directionality: Directionality
@@ -402,9 +388,41 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
402388
});
403389

404390
this.dir = this.directionality.value;
405-
this.directionality.change?.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
391+
this.directionality.change.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
406392
this.dir = direction;
407393
});
394+
395+
this.ngZone.runOutsideAngular(() => {
396+
fromEvent(this.inputElement.nativeElement, 'keyup')
397+
.pipe(takeUntil(this.destroy$))
398+
.subscribe(() => this.stop());
399+
400+
fromEvent<KeyboardEvent>(this.inputElement.nativeElement, 'keydown')
401+
.pipe(takeUntil(this.destroy$))
402+
.subscribe(event => {
403+
const { keyCode } = event;
404+
405+
if (keyCode !== UP_ARROW && keyCode !== DOWN_ARROW && keyCode !== ENTER) {
406+
return;
407+
}
408+
409+
this.ngZone.run(() => {
410+
if (keyCode === UP_ARROW) {
411+
const ratio = this.getRatio(event);
412+
this.up(event, ratio);
413+
this.stop();
414+
} else if (keyCode === DOWN_ARROW) {
415+
const ratio = this.getRatio(event);
416+
this.down(event, ratio);
417+
this.stop();
418+
} else {
419+
this.updateDisplayValue(this.value!);
420+
}
421+
422+
this.cdr.markForCheck();
423+
});
424+
});
425+
});
408426
}
409427

410428
ngOnChanges(changes: SimpleChanges): void {

components/input-number/input-number.spec.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
2-
import { Component, DebugElement, ViewChild } from '@angular/core';
1+
import { DOWN_ARROW, ENTER, TAB, UP_ARROW } from '@angular/cdk/keycodes';
2+
import { ApplicationRef, Component, DebugElement, NgZone, ViewChild } from '@angular/core';
33
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
44
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
55
import { By } from '@angular/platform-browser';
6+
import { take } from 'rxjs/operators';
67

78
import { createKeyboardEvent, dispatchEvent, dispatchFakeEvent } from 'ng-zorro-antd/core/testing';
89

@@ -395,6 +396,36 @@ describe('input number', () => {
395396
fixture.detectChanges();
396397
expect(inputNumber.nativeElement.classList).not.toContain('ant-input-number-focused');
397398
});
399+
describe('change detection behavior', () => {
400+
it('should not run change detection on keyup and keydown events', done => {
401+
const ngZone = TestBed.inject(NgZone);
402+
const appRef = TestBed.inject(ApplicationRef);
403+
spyOn(appRef, 'tick');
404+
spyOn(inputNumber.componentInstance, 'stop').and.callThrough();
405+
406+
inputElement.dispatchEvent(new KeyboardEvent('keyup'));
407+
expect(appRef.tick).toHaveBeenCalledTimes(0);
408+
expect(inputNumber.componentInstance.stop).toHaveBeenCalled();
409+
410+
inputElement.dispatchEvent(
411+
new KeyboardEvent('keydown', {
412+
keyCode: TAB
413+
})
414+
);
415+
expect(appRef.tick).toHaveBeenCalledTimes(0);
416+
417+
inputElement.dispatchEvent(
418+
new KeyboardEvent('keydown', {
419+
keyCode: ENTER
420+
})
421+
);
422+
423+
ngZone.onMicrotaskEmpty.pipe(take(1)).subscribe(() => {
424+
expect(appRef.tick).toHaveBeenCalledTimes(1);
425+
done();
426+
});
427+
});
428+
});
398429
});
399430

400431
describe('input number form', () => {

0 commit comments

Comments
 (0)