Skip to content

Commit 8ee1edc

Browse files
committed
fix(select): avoid going into infinite loop under certain conditions
Currently `md-select` calls `writeValue` recursively until the `QueryList` with all of the options is initialized. This usually means 1-2 iterations max, however in certain conditions (e.g. a sibling component throws an error on init) this may not happen and the browser would get thrown into an infinite loop. This change switches to saving the attempted value assignments in a property and applying it after initialization. Fixes #2950.
1 parent 2f10a95 commit 8ee1edc

File tree

1 file changed

+18
-8
lines changed

1 file changed

+18
-8
lines changed

src/lib/select/select.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
123123
/** The placeholder displayed in the trigger of the select. */
124124
private _placeholder: string;
125125

126+
/** Holds a value that was attempted to be assigned before the component was initialized. */
127+
private _tempValue: any;
128+
126129
/** The animation state of the placeholder. */
127130
_placeholderState = '';
128131

@@ -242,6 +245,15 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
242245
ngAfterContentInit() {
243246
this._initKeyManager();
244247
this._resetOptions();
248+
249+
// Assign any values that were deferred until the component is initialized.
250+
if (this._tempValue) {
251+
Promise.resolve(null).then(() => {
252+
this.writeValue(this._tempValue);
253+
this._tempValue = null;
254+
});
255+
}
256+
245257
this._changeSubscription = this.options.changes.subscribe(() => {
246258
this._resetOptions();
247259

@@ -290,16 +302,14 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
290302
* @param value New value to be written to the model.
291303
*/
292304
writeValue(value: any): void {
293-
if (!this.options) {
305+
if (this.options) {
306+
this._setSelectionByValue(value);
307+
} else {
294308
// In reactive forms, writeValue() will be called synchronously before
295-
// the select's child options have been created. It's necessary to call
296-
// writeValue() again after the options have been created to ensure any
297-
// initial view value is set.
298-
Promise.resolve(null).then(() => this.writeValue(value));
299-
return;
309+
// the select's child options have been created. We save the value and
310+
// assign it after everything is set up, in order to avoid lost data.
311+
this._tempValue = value;
300312
}
301-
302-
this._setSelectionByValue(value);
303313
}
304314

305315
/**

0 commit comments

Comments
 (0)