Skip to content

Commit

Permalink
fix(slide-toggle): NgControl not pristine if initial value is set
Browse files Browse the repository at this point in the history
* If a developer sets an initial value for the slide-toggle the NgControl of the slide-toggle will turn dirty. The slide-toggle should only turn dirty if the value changed after initialization.

Fixes angular#4968
  • Loading branch information
devversion committed Jun 8, 2017
1 parent 9c45865 commit 83c123c
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
17 changes: 16 additions & 1 deletion src/lib/slide-toggle/slide-toggle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,14 +402,29 @@ describe('MdSlideToggle', () => {
});

describe('custom template', () => {

it('should not trigger the change event on initialization', async(() => {
let fixture = TestBed.createComponent(SlideToggleTestApp);
const fixture = TestBed.createComponent(SlideToggleTestApp);
fixture.componentInstance.slideModel = true;
fixture.componentInstance.slideChecked = true;
fixture.detectChanges();

expect(fixture.componentInstance.lastEvent).toBeFalsy();
}));

it('should be pristine if initial value is set from NgModel', async(() => {
const fixture = TestBed.createComponent(SlideToggleTestApp);

fixture.componentInstance.slideModel = true;
fixture.detectChanges();

const slideToggleDebug = fixture.debugElement.query(By.css('md-slide-toggle'));
const slideToggleModel = slideToggleDebug.injector.get<NgModel>(NgModel);

fixture.whenStable().then(() => {
expect(slideToggleModel.pristine).toBe(true);
});
}));
});

describe('with forms', () => {
Expand Down
26 changes: 21 additions & 5 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AfterContentInit,
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Expand Down Expand Up @@ -67,8 +68,8 @@ export const _MdSlideToggleMixinBase = mixinColor(mixinDisabled(MdSlideToggleBas
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdSlideToggle extends _MdSlideToggleMixinBase
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable, CanColor {
export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy, AfterContentInit,
AfterViewInit, ControlValueAccessor, CanDisable, CanColor {
private onChange = (_: any) => {};
private onTouched = () => {};

Expand All @@ -79,6 +80,9 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
private _required: boolean = false;
private _disableRipple: boolean = false;

/** Whether the slide-toggle is initialized or not. */
private _isInitialized: boolean = false;

/** Reference to the focus state ripple. */
private _focusRipple: RippleRef;

Expand Down Expand Up @@ -137,6 +141,13 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
.subscribe(focusOrigin => this._onInputFocusChange(focusOrigin));
}

ngAfterViewInit() {
// If developers set an initial value from a NgControl the ControlValueAccessor takes another
// tick to reflect this change. The initialization property is necessary to avoid that the
// slide-toggle incorrectly updates the NgControl's state if a initial value is specified.
Promise.resolve().then(() => this._isInitialized = true);
}

ngOnDestroy() {
this._focusOriginMonitor.stopMonitoring(this._inputElement.nativeElement);
}
Expand Down Expand Up @@ -204,11 +215,16 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase

/** Whether the slide-toggle is checked. */
@Input()
get checked() { return !!this._checked; }
get checked() { return this._checked; }
set checked(value) {
if (this.checked !== !!value) {
this._checked = value;
this.onChange(this._checked);
this._checked = !!value;

// Only update the value of the ControlValueAccessor if the slide-toggle component is
// initialized and the NgModel has been initialized.
if (this._isInitialized) {
this.onChange(this._checked);
}
}
}

Expand Down

0 comments on commit 83c123c

Please sign in to comment.