Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit ec8183e

Browse files
authored
fix(switch): Exception thrown in foundation if disabled (#2111)
closes #2107
1 parent 23b1098 commit ec8183e

2 files changed

Lines changed: 65 additions & 19 deletions

File tree

packages/switch/switch.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ export class MdcSwitch extends MDCComponent<MDCSwitchFoundation> implements MdcF
8686
AfterViewInit, ControlValueAccessor, OnDestroy, MDCRippleCapableSurface {
8787
private _uniqueId: string = `mdc-switch-${++nextUniqueId}`;
8888

89+
private _initialized: boolean = false;
90+
8991
_root!: Element;
9092

9193
@Input() id: string = this._uniqueId;
@@ -148,7 +150,12 @@ export class MdcSwitch extends MDCComponent<MDCSwitchFoundation> implements MdcF
148150
return `${this.id || this._uniqueId}-input`;
149151
}
150152

151-
getDefaultFoundation() {
153+
getDefaultFoundation(): any {
154+
// Do not initialize foundation until ngAfterViewInit runs
155+
if (!this._initialized) {
156+
return undefined;
157+
}
158+
152159
const adapter: MDCSwitchAdapter = {
153160
addClass: (className: string) => this._getHostElement().classList.add(className),
154161
removeClass: (className: string) => this._getHostElement().classList.remove(className),
@@ -173,22 +180,29 @@ export class MdcSwitch extends MDCComponent<MDCSwitchFoundation> implements MdcF
173180
}
174181

175182
ngAfterViewInit(): void {
183+
this._initialized = true;
176184
this.ripple = this._createRipple();
177185
this.ripple.init();
178-
this._foundation.init();
186+
187+
this._asyncBuildFoundation()
188+
.then(() => {
189+
this._foundation.init();
190+
this.setDisabledState(this._inputElement.nativeElement.disabled);
191+
});
179192
}
180193

181194
ngOnDestroy(): void {
182195
this.ripple.destroy();
196+
this.destroy();
197+
}
198+
199+
async _asyncBuildFoundation(): Promise<void> {
200+
this._foundation = this.getDefaultFoundation();
183201
}
184202

185203
onChange(evt: Event): void {
186204
evt.stopPropagation();
187205

188-
if (this.disabled) {
189-
return;
190-
}
191-
192206
this._foundation.handleChange(evt);
193207
this._checked = this._inputElement.nativeElement.checked;
194208
this._foundation.setChecked(this._checked);
@@ -224,9 +238,13 @@ export class MdcSwitch extends MDCComponent<MDCSwitchFoundation> implements MdcF
224238
}
225239

226240
setDisabledState(disabled: boolean): void {
227-
this._disabled = coerceBooleanProperty(disabled);
228-
this._foundation.setDisabled(this._disabled);
229-
this._changeDetectorRef.markForCheck();
241+
const newValue = coerceBooleanProperty(disabled);
242+
243+
if (newValue !== this._disabled) {
244+
this._disabled = newValue;
245+
this._foundation?.setDisabled(newValue);
246+
this._changeDetectorRef.markForCheck();
247+
}
230248
}
231249

232250
focus(): void {

test/switch/switch.test.ts

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { Component, DebugElement } from '@angular/core';
2-
import { ComponentFixture, fakeAsync, flushMicrotasks, TestBed, tick, flush } from '@angular/core/testing';
3-
import { FormControl, FormsModule, NgModel, ReactiveFormsModule } from '@angular/forms';
4-
import { By } from '@angular/platform-browser';
1+
import {Component, DebugElement} from '@angular/core';
2+
import {ComponentFixture, fakeAsync, flushMicrotasks, TestBed, tick, flush, async} from '@angular/core/testing';
3+
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
4+
import {By} from '@angular/platform-browser';
55

6-
import { dispatchFakeEvent, dispatchMouseEvent } from '../testing/dispatch-events';
6+
import {dispatchFakeEvent, dispatchMouseEvent} from '../testing/dispatch-events';
77

8-
import { MdcSwitch, MdcSwitchModule } from '@angular-mdc/web';
8+
import {MdcSwitch, MdcSwitchModule} from '@angular-mdc/web';
99

1010
describe('MdcSwitch', () => {
1111
let fixture: ComponentFixture<any>;
@@ -16,13 +16,36 @@ describe('MdcSwitch', () => {
1616
declarations: [
1717
SingleSwitch,
1818
SwitchWithModel,
19-
SwitchWithFormControl
19+
SwitchWithFormControl,
20+
DisabledSwitch
2021
]
2122
});
2223

2324
TestBed.compileComponents();
2425
}));
2526

27+
describe('disabled switch', () => {
28+
let switchDebugElement: DebugElement;
29+
let switchNativeElement: HTMLElement;
30+
let switchInstance: MdcSwitch;
31+
let testComponent: DisabledSwitch;
32+
33+
beforeEach(async(() => {
34+
fixture = TestBed.createComponent(DisabledSwitch);
35+
fixture.detectChanges();
36+
37+
switchDebugElement = fixture.debugElement.query(By.directive(MdcSwitch));
38+
switchNativeElement = switchDebugElement.nativeElement;
39+
switchInstance = switchDebugElement.componentInstance;
40+
testComponent = fixture.debugElement.componentInstance;
41+
}));
42+
43+
it('#should be disabled', () => {
44+
fixture.detectChanges();
45+
expect(switchInstance._inputElement.nativeElement.disabled).toBe(true);
46+
});
47+
});
48+
2649
describe('basic behaviors', () => {
2750
let switchDebugElement: DebugElement;
2851
let switchNativeElement: HTMLElement;
@@ -243,7 +266,7 @@ describe('MdcSwitch with forms', () => {
243266
fixture.detectChanges();
244267
tick();
245268

246-
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({ checked: true }));
269+
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({checked: true}));
247270
subscription.unsubscribe();
248271
}));
249272

@@ -313,7 +336,7 @@ class SingleSwitch {
313336
slideTabindex: number;
314337
switchValue: string = 'single_switch';
315338

316-
onChange: () => void = () => { };
339+
onChange: () => void = () => {};
317340
}
318341

319342
@Component({
@@ -328,11 +351,16 @@ class SwitchWithFormControl {
328351
formControl = new FormControl();
329352
}
330353

354+
@Component({
355+
template: `<mdc-switch disabled></mdc-switch>`,
356+
})
357+
class DisabledSwitch {}
358+
331359
/** Simple component for testing with ngModel in a form. */
332360
@Component({
333361
template: `<mdc-switch name="cb" [(ngModel)]="modelValue" (change)="onChange()">Be good</mdc-switch>`,
334362
})
335363
class SwitchWithModel {
336364
modelValue: boolean = false;
337-
onChange: () => void = () => { };
365+
onChange: () => void = () => {};
338366
}

0 commit comments

Comments
 (0)