Skip to content

Commit

Permalink
perf(module:input-number): reduce change detection cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt committed Dec 18, 2021
1 parent 6d9b1ff commit dd0fcc6
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 21 deletions.
56 changes: 37 additions & 19 deletions components/input-number/input-number.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EventEmitter,
forwardRef,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
Expand All @@ -25,7 +26,7 @@ import {
ViewEncapsulation
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs';
import { fromEvent, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { BooleanInput, NzSizeLDSType, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types';
Expand Down Expand Up @@ -70,8 +71,6 @@ import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util';
[placeholder]="nzPlaceHolder"
[attr.step]="nzStep"
[attr.inputmode]="nzInputMode"
(keydown)="onKeyDown($event)"
(keyup)="stop()"
[ngModel]="displayValue"
(ngModelChange)="onModelChange($event)"
/>
Expand Down Expand Up @@ -335,20 +334,6 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
this.inputElement.nativeElement.value = `${displayValue}`;
}

onKeyDown(e: KeyboardEvent): void {
if (e.keyCode === UP_ARROW) {
const ratio = this.getRatio(e);
this.up(e, ratio);
this.stop();
} else if (e.keyCode === DOWN_ARROW) {
const ratio = this.getRatio(e);
this.down(e, ratio);
this.stop();
} else if (e.keyCode === ENTER) {
this.updateDisplayValue(this.value!);
}
}

writeValue(value: number): void {
this.value = value;
this.setValue(value);
Expand Down Expand Up @@ -378,7 +363,8 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
}

constructor(
private elementRef: ElementRef,
private ngZone: NgZone,
private elementRef: ElementRef<HTMLElement>,
private cdr: ChangeDetectorRef,
private focusMonitor: FocusMonitor,
@Optional() private directionality: Directionality
Expand All @@ -404,9 +390,41 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
});

this.dir = this.directionality.value;
this.directionality.change?.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
this.directionality.change.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
this.dir = direction;
});

this.ngZone.runOutsideAngular(() => {
fromEvent(this.inputElement.nativeElement, 'keyup')
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.stop());

fromEvent<KeyboardEvent>(this.inputElement.nativeElement, 'keydown')
.pipe(takeUntil(this.destroy$))
.subscribe(event => {
const { keyCode } = event;

if (keyCode !== UP_ARROW && keyCode !== DOWN_ARROW && keyCode !== ENTER) {
return;
}

this.ngZone.run(() => {
if (keyCode === UP_ARROW) {
const ratio = this.getRatio(event);
this.up(event, ratio);
this.stop();
} else if (keyCode === DOWN_ARROW) {
const ratio = this.getRatio(event);
this.down(event, ratio);
this.stop();
} else {
this.updateDisplayValue(this.value!);
}

this.cdr.markForCheck();
});
});
});
}

ngOnChanges(changes: SimpleChanges): void {
Expand Down
35 changes: 33 additions & 2 deletions components/input-number/input-number.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
import { Component, DebugElement, ViewChild } from '@angular/core';
import { DOWN_ARROW, ENTER, TAB, UP_ARROW } from '@angular/cdk/keycodes';
import { ApplicationRef, Component, DebugElement, NgZone, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { take } from 'rxjs/operators';

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

Expand Down Expand Up @@ -395,6 +396,36 @@ describe('input number', () => {
fixture.detectChanges();
expect(inputNumber.nativeElement.classList).not.toContain('ant-input-number-focused');
});
describe('change detection behavior', () => {
it('should not run change detection on keyup and keydown events', done => {
const ngZone = TestBed.inject(NgZone);
const appRef = TestBed.inject(ApplicationRef);
spyOn(appRef, 'tick');
spyOn(inputNumber.componentInstance, 'stop').and.callThrough();

inputElement.dispatchEvent(new KeyboardEvent('keyup'));
expect(appRef.tick).toHaveBeenCalledTimes(0);
expect(inputNumber.componentInstance.stop).toHaveBeenCalled();

inputElement.dispatchEvent(
new KeyboardEvent('keydown', {
keyCode: TAB
})
);
expect(appRef.tick).toHaveBeenCalledTimes(0);

inputElement.dispatchEvent(
new KeyboardEvent('keydown', {
keyCode: ENTER
})
);

ngZone.onMicrotaskEmpty.pipe(take(1)).subscribe(() => {
expect(appRef.tick).toHaveBeenCalledTimes(1);
done();
});
});
});
});

describe('input number form', () => {
Expand Down

0 comments on commit dd0fcc6

Please sign in to comment.