Skip to content

Commit bfdef8e

Browse files
authored
Revert "fix(slide-toggle): invalid model change event (#4140)" (#4218)
This reverts commit 317952a.
1 parent 66e65c4 commit bfdef8e

File tree

2 files changed

+35
-34
lines changed

2 files changed

+35
-34
lines changed

src/lib/slide-toggle/slide-toggle.spec.ts

+11-14
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,18 @@ describe('MdSlideToggle', () => {
295295
expect(slideToggleControl.pristine).toBe(true);
296296
expect(slideToggleControl.touched).toBe(false);
297297

298-
// After changing the value from the view, the control should
299-
// become dirty (not pristine), but remain untouched if focus is still there.
298+
// After changing the value programmatically, the control should
299+
// become dirty (not pristine), but remain untouched.
300300
slideToggle.checked = true;
301-
302-
// Dispatch a change event on the input element to fake a user interaction that triggered
303-
// the state change.
304-
dispatchFakeEvent(inputElement, 'change');
305-
306301
fixture.detectChanges();
307302

308303
expect(slideToggleControl.valid).toBe(true);
309304
expect(slideToggleControl.pristine).toBe(false);
310305
expect(slideToggleControl.touched).toBe(false);
311306

312-
// Once the input element looses focus, the control should remain dirty but should
313-
// also turn touched.
314-
dispatchFakeEvent(inputElement, 'blur');
307+
// After a user interaction occurs (such as a click), the control should remain dirty and
308+
// now also be touched.
309+
labelElement.click();
315310
fixture.detectChanges();
316311

317312
expect(slideToggleControl.valid).toBe(true);
@@ -329,13 +324,13 @@ describe('MdSlideToggle', () => {
329324
expect(slideToggleControl.touched).toBe(false);
330325
expect(slideToggleElement.classList).toContain('mat-checked');
331326

332-
// Once the input element looses focus, the control should remain dirty but should
333-
// also turn touched.
334-
dispatchFakeEvent(inputElement, 'blur');
327+
// After a user interaction occurs (such as a click), the control should remain dirty and
328+
// now also be touched.
329+
inputElement.click();
335330
fixture.detectChanges();
336331

337332
expect(slideToggleControl.touched).toBe(true);
338-
expect(slideToggleElement.classList).toContain('mat-checked');
333+
expect(slideToggleElement.classList).not.toContain('mat-checked');
339334
});
340335

341336
// TODO(kara): update when core/testing adds fix
@@ -439,13 +434,15 @@ describe('MdSlideToggle', () => {
439434
}));
440435

441436
it('should prevent the form from submit when being required', () => {
437+
442438
if ('reportValidity' in inputElement === false) {
443439
// If the browser does not report the validity then the tests will break.
444440
// e.g Safari 8 on Mobile.
445441
return;
446442
}
447443

448444
testComponent.isRequired = true;
445+
449446
fixture.detectChanges();
450447

451448
buttonElement.click();

src/lib/slide-toggle/slide-toggle.ts

+24-20
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
6464

6565
// A unique id for the slide-toggle. By default the id is auto-generated.
6666
private _uniqueId = `md-slide-toggle-${++nextId}`;
67+
private _checked: boolean = false;
6768
private _color: string;
6869
private _slideRenderer: SlideToggleRenderer = null;
6970
private _disabled: boolean = false;
@@ -85,9 +86,6 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
8586
/** Whether the label should appear after or before the slide-toggle. Defaults to 'after' */
8687
@Input() labelPosition: 'before' | 'after' = 'after';
8788

88-
/** Whether the slide-toggle element is checked or not */
89-
@Input() checked: boolean = false;
90-
9189
/** Used to set the aria-label attribute on the underlying input element. */
9290
@Input('aria-label') ariaLabel: string = null;
9391

@@ -138,30 +136,29 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
138136
}
139137

140138
/**
141-
* This function will called if the underlying input changed its value through user interaction.
139+
* The onChangeEvent method will be also called on click.
140+
* This is because everything for the slide-toggle is wrapped inside of a label,
141+
* which triggers a onChange event on click.
142142
*/
143143
_onChangeEvent(event: Event) {
144144
// We always have to stop propagation on the change event.
145145
// Otherwise the change event, from the input element, will bubble up and
146146
// emit its event object to the component's `change` output.
147147
event.stopPropagation();
148148

149-
// Sync the value from the underlying input element with the slide-toggle component.
150-
this.checked = this._inputElement.nativeElement.checked;
149+
// Once a drag is currently in progress, we do not want to toggle the slide-toggle on a click.
150+
if (!this.disabled && !this._slideRenderer.dragging) {
151+
this.toggle();
151152

152-
// Emit our custom change event if the native input emitted one.
153-
// It is important to only emit it, if the native input triggered one, because we don't want
154-
// to trigger a change event, when the `checked` variable changes programmatically.
155-
this._emitChangeEvent();
153+
// Emit our custom change event if the native input emitted one.
154+
// It is important to only emit it, if the native input triggered one, because
155+
// we don't want to trigger a change event, when the `checked` variable changes for example.
156+
this._emitChangeEvent();
157+
}
156158
}
157159

158160
_onInputClick(event: Event) {
159-
// In some situations the user will release the mouse on the label element. The label element
160-
// redirects the click to the underlying input element and will result in a value change.
161-
// Prevent the default behavior if dragging, because the value will be set after drag.
162-
if (this._slideRenderer.dragging) {
163-
event.preventDefault();
164-
}
161+
this.onTouched();
165162

166163
// We have to stop propagation for click events on the visual hidden input element.
167164
// By default, when a user clicks on a label element, a generated click event will be
@@ -198,6 +195,16 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
198195
this._focusOriginMonitor.focusVia(this._inputElement.nativeElement, this._renderer, 'keyboard');
199196
}
200197

198+
/** Whether the slide-toggle is checked. */
199+
@Input()
200+
get checked() { return !!this._checked; }
201+
set checked(value) {
202+
if (this.checked !== !!value) {
203+
this._checked = value;
204+
this.onChange(this._checked);
205+
}
206+
}
207+
201208
/** The color of the slide-toggle. Can be primary, accent, or warn. */
202209
@Input()
203210
get color(): string { return this._color; }
@@ -238,15 +245,12 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
238245
}
239246
}
240247

241-
/**
242-
* Emits a change event on the `change` output. Also notifies the FormControl about the change.
243-
*/
248+
/** Emits the change event to the `change` output EventEmitter */
244249
private _emitChangeEvent() {
245250
let event = new MdSlideToggleChange();
246251
event.source = this;
247252
event.checked = this.checked;
248253
this.change.emit(event);
249-
this.onChange(this.checked);
250254
}
251255

252256

0 commit comments

Comments
 (0)