diff --git a/src/demo-app/select/select-demo.ts b/src/demo-app/select/select-demo.ts index f7bec82ca09d..7bd1c6cda590 100644 --- a/src/demo-app/select/select-demo.ts +++ b/src/demo-app/select/select-demo.ts @@ -11,7 +11,7 @@ export class SelectDemo { isRequired = false; isDisabled = false; currentDrink: string; - foodControl = new FormControl(''); + foodControl = new FormControl('pizza-1'); foods = [ {value: 'steak-0', viewValue: 'Steak'}, diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 3e05ceffe0fa..1c0dddabc878 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -244,8 +244,29 @@ describe('MdSelect', () => { beforeEach(() => { fixture = TestBed.createComponent(BasicSelect); + }); + + it('should take an initial view value with reactive forms', () => { + fixture.componentInstance.control = new FormControl('pizza-1'); + fixture.detectChanges(); + + const value = fixture.debugElement.query(By.css('.md-select-value')); + expect(value.nativeElement.textContent) + .toContain('Pizza', `Expected trigger to be populated by the control's initial value.`); + + trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement; + trigger.click(); fixture.detectChanges(); + const options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + expect(options[1].classList) + .toContain('md-selected', + `Expected option with the control's initial value to be selected.`); + }); + + beforeEach(() => { + fixture.detectChanges(); trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement; }); diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 19ae9817c9da..90af646afe68 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -5,6 +5,7 @@ import { ElementRef, EventEmitter, Input, + NgZone, OnDestroy, Optional, Output, @@ -133,7 +134,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr @Output() onClose = new EventEmitter(); constructor(private _element: ElementRef, private _renderer: Renderer, - @Optional() private _dir: Dir, @Optional() public _control: NgControl) { + @Optional() private _dir: Dir, @Optional() public _control: NgControl, + private _ngZone: NgZone) { if (this._control) { this._control.valueAccessor = this; } @@ -175,7 +177,14 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr * required to integrate with Angular's core forms API. */ writeValue(value: any): void { - if (!this.options) { return; } + if (!this.options) { + // In reactive forms, writeValue() will be called synchronously before + // the select's child options have been created. It's necessary to call + // writeValue() again after the options have been created to ensure any + // initial view value is set. + this._ngZone.onStable.first().subscribe(() => this.writeValue(value)); + return; + } this.options.forEach((option: MdOption) => { if (option.value === value) {