diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts
index bbc35dece8dc..103d0474a7e3 100644
--- a/src/lib/dialog/dialog.spec.ts
+++ b/src/lib/dialog/dialog.spec.ts
@@ -381,6 +381,8 @@ describe('MdDialog', () => {
expect(document.activeElement.id)
.toBe('dialog-trigger', 'Expected that the trigger was refocused after dialog close');
+
+ document.body.removeChild(button);
}));
});
diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts
index f618cf5b88b1..59c2e5de58da 100644
--- a/src/lib/select/select.spec.ts
+++ b/src/lib/select/select.spec.ts
@@ -7,6 +7,7 @@ import {
ViewChild,
ViewChildren,
ChangeDetectionStrategy,
+ OnInit,
} from '@angular/core';
import {MdSelectModule} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
@@ -34,6 +35,8 @@ describe('MdSelect', () => {
SelectWithChangeEvent,
CustomSelectAccessor,
CompWithCustomSelect,
+ SelectWithErrorSibling,
+ ThrowsErrorOnInit,
BasicSelectOnPush
],
providers: [
@@ -1239,6 +1242,14 @@ describe('MdSelect', () => {
});
}));
+ it('should not crash the browser when a sibling throws an error on init', async(() => {
+ // Note that this test can be considered successful if the error being thrown didn't
+ // end up crashing the testing setup altogether.
+ expect(() => {
+ TestBed.createComponent(SelectWithErrorSibling).detectChanges();
+ }).toThrowError(new RegExp('Oh no!', 'g'));
+ }));
+
});
describe('change event', () => {
@@ -1281,7 +1292,7 @@ describe('MdSelect', () => {
beforeEach(() => {
fixture = TestBed.createComponent(BasicSelectOnPush);
fixture.detectChanges();
- trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
+ trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
});
it('should update the trigger based on the value', () => {
@@ -1474,6 +1485,27 @@ class CompWithCustomSelect {
@ViewChild(CustomSelectAccessor) customAccessor: CustomSelectAccessor;
}
+@Component({
+ selector: 'select-infinite-loop',
+ template: `
+
+
+ `
+})
+class SelectWithErrorSibling {
+ value: string;
+}
+
+@Component({
+ selector: 'throws-error-on-init',
+ template: ''
+})
+export class ThrowsErrorOnInit implements OnInit {
+ ngOnInit() {
+ throw new Error('Oh no!');
+ }
+}
+
@Component({
selector: 'basic-select-on-push',
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts
index 9d99134b0a34..c1c0a3e37978 100644
--- a/src/lib/select/select.ts
+++ b/src/lib/select/select.ts
@@ -25,6 +25,8 @@ import {ControlValueAccessor, NgControl} from '@angular/forms';
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {ConnectedOverlayDirective} from '../core/overlay/overlay-directives';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
+import 'rxjs/add/operator/startWith';
+
/**
* The following style constants are necessary to save here in order
@@ -243,8 +245,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
ngAfterContentInit() {
this._initKeyManager();
- this._resetOptions();
- this._changeSubscription = this.options.changes.subscribe(() => {
+
+ this._changeSubscription = this.options.changes.startWith(null).subscribe(() => {
this._resetOptions();
if (this._control) {
@@ -257,8 +259,14 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
ngOnDestroy() {
this._dropSubscriptions();
- this._changeSubscription.unsubscribe();
- this._tabSubscription.unsubscribe();
+
+ if (this._changeSubscription) {
+ this._changeSubscription.unsubscribe();
+ }
+
+ if (this._tabSubscription) {
+ this._tabSubscription.unsubscribe();
+ }
}
/** Toggles the overlay panel open or closed. */
@@ -292,17 +300,10 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
* @param value New value to be written to the model.
*/
writeValue(value: any): void {
- 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.
- Promise.resolve(null).then(() => this.writeValue(value));
- return;
+ if (this.options) {
+ this._setSelectionByValue(value);
+ this._changeDetectorRef.markForCheck();
}
-
- this._setSelectionByValue(value);
- this._changeDetectorRef.markForCheck();
}
/**