Skip to content

Commit

Permalink
fix(form-field): infinite loop when using outline appearance and elem…
Browse files Browse the repository at this point in the history
…ent isn't in the DOM (#11406)

Prevents the form field with an `outline` appearance from going into an infinite change detection loop if the form field is active, but isn't in the DOM. The issue comes from the fact that we've got a `Promise.resolve` inside an `AfterContentChecked` hook, which will kick off another round of change detection, causing the lifecycle hook to be called again.

Fixes #11329.
  • Loading branch information
crisbeto authored and mmalerba committed May 24, 2018
1 parent cdbb3e1 commit e592615
Showing 1 changed file with 18 additions and 5 deletions.
23 changes: 18 additions & 5 deletions src/lib/form-field/form-field.ts
Expand Up @@ -21,6 +21,7 @@ import {
Inject,
InjectionToken,
Input,
NgZone,
Optional,
QueryList,
ViewChild,
Expand Down Expand Up @@ -233,8 +234,9 @@ export class MatFormField extends _MatFormFieldMixinBase
@Optional() private _dir: Directionality,
@Optional() @Inject(MAT_FORM_FIELD_DEFAULT_OPTIONS) private _defaultOptions:
MatFormFieldDefaultOptions,
// @deletion-target 7.0.0 _platform to be made required.
private _platform?: Platform) {
// @deletion-target 7.0.0 _platform and _ngZone to be made required.
private _platform?: Platform,
private _ngZone?: NgZone) {
super(_elementRef);

this._labelOptions = labelOptions ? labelOptions : {};
Expand Down Expand Up @@ -283,8 +285,19 @@ export class MatFormField extends _MatFormFieldMixinBase

ngAfterContentChecked() {
this._validateControlChild();

if (!this._initialGapCalculated) {
Promise.resolve().then(() => this.updateOutlineGap());
// @deletion-target 7.0.0 Remove this check and else block once _ngZone is required.
if (this._ngZone) {
// It's important that we run this outside the `_ngZone`, because the `Promise.resolve`
// can kick us into an infinite change detection loop, if the `_initialGapCalculated`
// wasn't flipped on for some reason.
this._ngZone.runOutsideAngular(() => {
Promise.resolve().then(() => this.updateOutlineGap());
});
} else {
Promise.resolve().then(() => this.updateOutlineGap());
}
}
}

Expand All @@ -296,8 +309,8 @@ export class MatFormField extends _MatFormFieldMixinBase

/** Determines whether a class from the NgControl should be forwarded to the host element. */
_shouldForward(prop: string): boolean {
let ngControl = this._control ? this._control.ngControl : null;
return ngControl && (ngControl as any)[prop];
const ngControl = this._control ? this._control.ngControl : null;
return ngControl && ngControl[prop];
}

_hasPlaceholder() {
Expand Down

0 comments on commit e592615

Please sign in to comment.