diff --git a/src/lib/form-field/form-field.ts b/src/lib/form-field/form-field.ts index 0da9926aa78..a4e88d549f8 100644 --- a/src/lib/form-field/form-field.ts +++ b/src/lib/form-field/form-field.ts @@ -34,7 +34,7 @@ import { MAT_LABEL_GLOBAL_OPTIONS, mixinColor, } from '@angular/material/core'; -import {EMPTY, fromEvent, merge} from 'rxjs'; +import {fromEvent, merge} from 'rxjs'; import {startWith, take} from 'rxjs/operators'; import {MatError} from './error'; import {matFormFieldAnimations} from './form-field-animations'; @@ -154,14 +154,7 @@ export class MatFormField extends _MatFormFieldMixinBase this._appearance = value || (this._defaults && this._defaults.appearance) || 'legacy'; if (this._appearance === 'outline' && oldValue !== value) { - // @breaking-change 7.0.0 Remove this check and else block once _ngZone is required. - if (this._ngZone) { - this._ngZone!.onStable.pipe(take(1)).subscribe(() => { - this._ngZone!.runOutsideAngular(() => this.updateOutlineGap()); - }); - } else { - Promise.resolve().then(() => this.updateOutlineGap()); - } + this._updateOutlineGapOnStable(); } } _appearance: MatFormFieldAppearance; @@ -274,22 +267,30 @@ export class MatFormField extends _MatFormFieldMixinBase ngAfterContentInit() { this._validateControlChild(); - if (this._control.controlType) { - this._elementRef.nativeElement.classList - .add(`mat-form-field-type-${this._control.controlType}`); + + const control = this._control; + + if (control.controlType) { + this._elementRef.nativeElement.classList.add(`mat-form-field-type-${control.controlType}`); } // Subscribe to changes in the child control state in order to update the form field UI. - this._control.stateChanges.pipe(startWith(null!)).subscribe(() => { + control.stateChanges.pipe(startWith(null!)).subscribe(() => { this._validatePlaceholders(); this._syncDescribedByIds(); this._changeDetectorRef.markForCheck(); }); - // Run change detection if the value, prefix, or suffix changes. - const valueChanges = this._control.ngControl && this._control.ngControl.valueChanges || EMPTY; - merge(valueChanges, this._prefixChildren.changes, this._suffixChildren.changes) - .subscribe(() => this._changeDetectorRef.markForCheck()); + // Run change detection if the value changes. + if (control.ngControl && control.ngControl.valueChanges) { + control.ngControl.valueChanges.subscribe(() => this._changeDetectorRef.markForCheck()); + } + + // Run change detection and update the outline if the suffix or prefix changes. + merge(this._prefixChildren.changes, this._suffixChildren.changes).subscribe(() => { + this._updateOutlineGapOnStable(); + this._changeDetectorRef.markForCheck(); + }); // Re-validate when the number of hints changes. this._hintChildren.changes.pipe(startWith(null)).subscribe(() => { @@ -504,4 +505,14 @@ export class MatFormField extends _MatFormFieldMixinBase private _getStartEnd(rect: ClientRect): number { return this._dir && this._dir.value === 'rtl' ? rect.right : rect.left; } + + /** Updates the outline gap the new time the zone stabilizes. */ + private _updateOutlineGapOnStable() { + // @breaking-change 7.0.0 Remove this check and else block once _ngZone is required. + if (this._ngZone) { + this._ngZone.onStable.pipe(take(1)).subscribe(() => this.updateOutlineGap()); + } else { + Promise.resolve().then(() => this.updateOutlineGap()); + } + } } diff --git a/src/lib/input/input.spec.ts b/src/lib/input/input.spec.ts index 43304897295..ffb83f81ff4 100644 --- a/src/lib/input/input.spec.ts +++ b/src/lib/input/input.spec.ts @@ -1319,6 +1319,28 @@ describe('MatInput with appearance', () => { expect(parseInt(outlineGap.style.width)).toBeGreaterThan(0); })); + it('should update the outline gap when the prefix/suffix is added or removed', fakeAsync(() => { + fixture.destroy(); + TestBed.resetTestingModule(); + + const outlineFixture = createComponent(MatInputWithAppearanceAndLabel); + + outlineFixture.componentInstance.appearance = 'outline'; + outlineFixture.detectChanges(); + flush(); + outlineFixture.detectChanges(); + + spyOn(outlineFixture.componentInstance.formField, 'updateOutlineGap'); + + outlineFixture.componentInstance.showPrefix = true; + outlineFixture.detectChanges(); + flush(); + outlineFixture.detectChanges(); + + expect(outlineFixture.componentInstance.formField.updateOutlineGap).toHaveBeenCalled(); + })); + + }); describe('MatFormField default options', () => { @@ -1774,13 +1796,16 @@ class MatInputWithAppearance { @Component({ template: ` + Somewhat long prefix {{labelContent}} ` }) class MatInputWithAppearanceAndLabel { + @ViewChild(MatFormField) formField: MatFormField; appearance: MatFormFieldAppearance; + showPrefix: boolean; labelContent = 'Label'; }