Skip to content

Commit

Permalink
perf(module:input-number): do not run change detection when mouseup
Browse files Browse the repository at this point in the history
… and `mouseleave` events are dispatched on handlers (#7313)
  • Loading branch information
arturovt committed Apr 21, 2022
1 parent 561041c commit 54386ef
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 25 deletions.
63 changes: 39 additions & 24 deletions components/input-number/input-number.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import {
ViewEncapsulation
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { fromEvent, Subject } from 'rxjs';
import { fromEvent, merge } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { NzDestroyService } from 'ng-zorro-antd/core/services';
import { BooleanInput, NzSizeLDSType, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types';
import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util';

Expand All @@ -38,21 +39,19 @@ import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util';
template: `
<div class="ant-input-number-handler-wrap">
<span
#upHandler
unselectable="unselectable"
class="ant-input-number-handler ant-input-number-handler-up"
(mousedown)="up($event)"
(mouseup)="stop()"
(mouseleave)="stop()"
[class.ant-input-number-handler-up-disabled]="disabledUp"
>
<i nz-icon nzType="up" class="ant-input-number-handler-up-inner"></i>
</span>
<span
#downHandler
unselectable="unselectable"
class="ant-input-number-handler ant-input-number-handler-down"
(mousedown)="down($event)"
(mouseup)="stop()"
(mouseleave)="stop()"
[class.ant-input-number-handler-down-disabled]="disabledDown"
>
<i nz-icon nzType="down" class="ant-input-number-handler-down-inner"></i>
Expand Down Expand Up @@ -82,7 +81,8 @@ import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util';
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NzInputNumberComponent),
multi: true
}
},
NzDestroyService
],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
Expand All @@ -104,7 +104,6 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
private autoStepTimer?: number;
private parsedValue?: string | number;
private value?: number;
private destroy$ = new Subject<void>();
displayValue?: string | number;
isFocused = false;
disabledUp = false;
Expand All @@ -114,6 +113,11 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
onTouched: OnTouchedType = () => {};
@Output() readonly nzBlur = new EventEmitter();
@Output() readonly nzFocus = new EventEmitter();
/** The native `<span class="ant-input-number-handler-up"></span>` element. */
@ViewChild('upHandler', { static: true }) upHandler!: ElementRef<HTMLElement>;
/** The native `<span class="ant-input-number-handler-down"></span>` element. */
@ViewChild('downHandler', { static: true }) downHandler!: ElementRef<HTMLElement>;
/** The native `<input class="ant-input-number-input" />` element. */
@ViewChild('inputElement', { static: true }) inputElement!: ElementRef<HTMLInputElement>;
@Input() nzSize: NzSizeLDSType = 'default';
@Input() nzMin: number = -Infinity;
Expand Down Expand Up @@ -372,30 +376,30 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn
private elementRef: ElementRef<HTMLElement>,
private cdr: ChangeDetectorRef,
private focusMonitor: FocusMonitor,
@Optional() private directionality: Directionality
@Optional() private directionality: Directionality,
private destroy$: NzDestroyService
) {}

ngOnInit(): void {
this.focusMonitor
.monitor(this.elementRef, true)
.pipe(takeUntil(this.destroy$))
.subscribe(focusOrigin => {
if (!focusOrigin) {
this.isFocused = false;
this.updateDisplayValue(this.value!);
this.nzBlur.emit();
Promise.resolve().then(() => this.onTouched());
} else {
this.isFocused = true;
this.nzFocus.emit();
}
});
this.focusMonitor.monitor(this.elementRef, true).subscribe(focusOrigin => {
if (!focusOrigin) {
this.isFocused = false;
this.updateDisplayValue(this.value!);
this.nzBlur.emit();
Promise.resolve().then(() => this.onTouched());
} else {
this.isFocused = true;
this.nzFocus.emit();
}
});

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

this.setupHandlersListeners();

this.ngZone.runOutsideAngular(() => {
fromEvent(this.inputElement.nativeElement, 'keyup')
.pipe(takeUntil(this.destroy$))
Expand Down Expand Up @@ -445,7 +449,18 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn

ngOnDestroy(): void {
this.focusMonitor.stopMonitoring(this.elementRef);
this.destroy$.next();
this.destroy$.complete();
}

private setupHandlersListeners(): void {
this.ngZone.runOutsideAngular(() => {
merge(
fromEvent(this.upHandler.nativeElement, 'mouseup'),
fromEvent(this.upHandler.nativeElement, 'mouseleave'),
fromEvent(this.downHandler.nativeElement, 'mouseup'),
fromEvent(this.downHandler.nativeElement, 'mouseleave')
)
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.stop());
});
}
}
20 changes: 19 additions & 1 deletion components/input-number/input-number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angul
import { By } from '@angular/platform-browser';
import { take } from 'rxjs/operators';

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

import { NzInputNumberComponent } from './input-number.component';
import { NzInputNumberModule } from './input-number.module';
Expand Down Expand Up @@ -429,6 +429,24 @@ describe('input number', () => {
done();
});
});
it('should not run change detection when `mouseup` and `mouseleave` events are dispatched on handlers', () => {
const appRef = TestBed.inject(ApplicationRef);
spyOn(appRef, 'tick');
spyOn(inputNumber.componentInstance, 'stop').and.callThrough();

const mouseupEvent = createMouseEvent('mouseup');
const mouseleaveEvent = createMouseEvent('mouseleave');

upHandler.dispatchEvent(mouseupEvent);
upHandler.dispatchEvent(mouseleaveEvent);

downHandler.dispatchEvent(mouseupEvent);
downHandler.dispatchEvent(mouseleaveEvent);

expect(appRef.tick).not.toHaveBeenCalled();
// We have dispatched 4 events that are followed by calling `stop()`.
expect(inputNumber.componentInstance.stop).toHaveBeenCalledTimes(4);
});
});
});

Expand Down

0 comments on commit 54386ef

Please sign in to comment.