diff --git a/components/switch/switch.component.ts b/components/switch/switch.component.ts
index 67f67dab9a..85e42c6453 100644
--- a/components/switch/switch.component.ts
+++ b/components/switch/switch.component.ts
@@ -14,6 +14,7 @@ import {
ElementRef,
forwardRef,
Input,
+ NgZone,
OnDestroy,
OnInit,
Optional,
@@ -22,7 +23,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 { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';
@@ -57,7 +58,6 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'switch';
[class.ant-switch-small]="nzSize === 'small'"
[class.ant-switch-rtl]="dir === 'rtl'"
[nzWaveExtraNode]="true"
- (keydown)="onKeyDown($event)"
>
@@ -72,10 +72,7 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'switch';
- `,
- host: {
- '(click)': 'onHostClick($event)'
- }
+ `
})
export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, OnDestroy, OnInit {
readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;
@@ -87,7 +84,7 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O
isChecked = false;
onChange: OnChangeType = () => {};
onTouched: OnTouchedType = () => {};
- @ViewChild('switchElement', { static: true }) private switchElement?: ElementRef;
+ @ViewChild('switchElement', { static: true }) switchElement!: ElementRef;
@Input() @InputBoolean() nzLoading = false;
@Input() @InputBoolean() nzDisabled = false;
@Input() @InputBoolean() nzControl = false;
@@ -99,13 +96,6 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O
private destroy$ = new Subject();
- onHostClick(e: MouseEvent): void {
- e.preventDefault();
- if (!this.nzDisabled && !this.nzLoading && !this.nzControl) {
- this.updateValue(!this.isChecked);
- }
- }
-
updateValue(value: boolean): void {
if (this.isChecked !== value) {
this.isChecked = value;
@@ -113,43 +103,74 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O
}
}
- onKeyDown(e: KeyboardEvent): void {
- if (!this.nzControl && !this.nzDisabled && !this.nzLoading) {
- if (e.keyCode === LEFT_ARROW) {
- this.updateValue(false);
- e.preventDefault();
- } else if (e.keyCode === RIGHT_ARROW) {
- this.updateValue(true);
- e.preventDefault();
- } else if (e.keyCode === SPACE || e.keyCode === ENTER) {
- this.updateValue(!this.isChecked);
- e.preventDefault();
- }
- }
- }
-
focus(): void {
- this.focusMonitor.focusVia(this.switchElement?.nativeElement, 'keyboard');
+ this.focusMonitor.focusVia(this.switchElement.nativeElement, 'keyboard');
}
blur(): void {
- this.switchElement?.nativeElement.blur();
+ this.switchElement.nativeElement.blur();
}
constructor(
public nzConfigService: NzConfigService,
+ private host: ElementRef,
+ private ngZone: NgZone,
private cdr: ChangeDetectorRef,
private focusMonitor: FocusMonitor,
@Optional() private directionality: Directionality
) {}
ngOnInit(): void {
- this.directionality.change?.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
+ this.directionality.change.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => {
this.dir = direction;
this.cdr.detectChanges();
});
this.dir = this.directionality.value;
+
+ this.ngZone.runOutsideAngular(() => {
+ fromEvent(this.host.nativeElement, 'click')
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(event => {
+ event.preventDefault();
+
+ if (this.nzControl || this.nzDisabled || this.nzLoading) {
+ return;
+ }
+
+ this.ngZone.run(() => {
+ this.updateValue(!this.isChecked);
+ this.cdr.markForCheck();
+ });
+ });
+
+ fromEvent(this.switchElement.nativeElement, 'keydown')
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(event => {
+ if (this.nzControl || this.nzDisabled || this.nzLoading) {
+ return;
+ }
+
+ const { keyCode } = event;
+ if (keyCode !== LEFT_ARROW && keyCode !== RIGHT_ARROW && keyCode !== SPACE && keyCode !== ENTER) {
+ return;
+ }
+
+ event.preventDefault();
+
+ this.ngZone.run(() => {
+ if (keyCode === LEFT_ARROW) {
+ this.updateValue(false);
+ } else if (keyCode === RIGHT_ARROW) {
+ this.updateValue(true);
+ } else if (keyCode === SPACE || keyCode === ENTER) {
+ this.updateValue(!this.isChecked);
+ }
+
+ this.cdr.markForCheck();
+ });
+ });
+ });
}
ngAfterViewInit(): void {
diff --git a/components/switch/switch.spec.ts b/components/switch/switch.spec.ts
index da65855ac1..c8ab59d979 100644
--- a/components/switch/switch.spec.ts
+++ b/components/switch/switch.spec.ts
@@ -1,6 +1,6 @@
import { BidiModule, Dir } from '@angular/cdk/bidi';
import { ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE } from '@angular/cdk/keycodes';
-import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core';
+import { ApplicationRef, Component, DebugElement, TemplateRef, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
@@ -172,6 +172,48 @@ describe('switch', () => {
fixture.detectChanges();
expect(switchElement.nativeElement.firstElementChild === document.activeElement).toBe(false);
});
+ describe('change detection behavior', () => {
+ it('should not run change detection on `click` events if the switch is disabled', () => {
+ testComponent.disabled = true;
+ fixture.detectChanges();
+
+ const appRef = TestBed.inject(ApplicationRef);
+ const event = new MouseEvent('click');
+
+ spyOn(appRef, 'tick');
+ spyOn(event, 'preventDefault').and.callThrough();
+
+ switchElement.nativeElement.dispatchEvent(event);
+
+ expect(appRef.tick).not.toHaveBeenCalled();
+ expect(event.preventDefault).toHaveBeenCalled();
+ });
+ it('should not run change detection on `keydown` events if the switch is disabled', () => {
+ testComponent.disabled = true;
+ fixture.detectChanges();
+
+ const switchButton = switchElement.nativeElement.querySelector('.ant-switch');
+ const appRef = TestBed.inject(ApplicationRef);
+ const event = new KeyboardEvent('keydown', {
+ keyCode: SPACE
+ });
+
+ spyOn(appRef, 'tick');
+ spyOn(event, 'preventDefault').and.callThrough();
+
+ switchButton.dispatchEvent(event);
+
+ expect(appRef.tick).not.toHaveBeenCalled();
+ expect(event.preventDefault).not.toHaveBeenCalled();
+
+ testComponent.disabled = false;
+ fixture.detectChanges();
+
+ switchButton.dispatchEvent(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ });
+ });
});
describe('template switch', () => {
let fixture: ComponentFixture;