Skip to content

Commit d588d93

Browse files
committed
fix(form-field): not updating outline when prefix/suffix is added or removed
Fixes the form field's outline not reacting to the prefix/suffix being toggled after init. Fixes #13251.
1 parent 3bc52df commit d588d93

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

src/lib/form-field/form-field.ts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
MAT_LABEL_GLOBAL_OPTIONS,
3535
mixinColor,
3636
} from '@angular/material/core';
37-
import {EMPTY, fromEvent, merge} from 'rxjs';
37+
import {fromEvent, merge} from 'rxjs';
3838
import {startWith, take} from 'rxjs/operators';
3939
import {MatError} from './error';
4040
import {matFormFieldAnimations} from './form-field-animations';
@@ -153,14 +153,7 @@ export class MatFormField extends _MatFormFieldMixinBase
153153
this._appearance = value || (this._defaults && this._defaults.appearance) || 'legacy';
154154

155155
if (this._appearance === 'outline' && oldValue !== value) {
156-
// @breaking-change 7.0.0 Remove this check and else block once _ngZone is required.
157-
if (this._ngZone) {
158-
this._ngZone!.onStable.pipe(take(1)).subscribe(() => {
159-
this._ngZone!.runOutsideAngular(() => this.updateOutlineGap());
160-
});
161-
} else {
162-
Promise.resolve().then(() => this.updateOutlineGap());
163-
}
156+
this._updateOutlineGapOnStable();
164157
}
165158
}
166159
_appearance: MatFormFieldAppearance;
@@ -273,22 +266,30 @@ export class MatFormField extends _MatFormFieldMixinBase
273266

274267
ngAfterContentInit() {
275268
this._validateControlChild();
276-
if (this._control.controlType) {
277-
this._elementRef.nativeElement.classList
278-
.add(`mat-form-field-type-${this._control.controlType}`);
269+
270+
const control = this._control;
271+
272+
if (control.controlType) {
273+
this._elementRef.nativeElement.classList.add(`mat-form-field-type-${control.controlType}`);
279274
}
280275

281276
// Subscribe to changes in the child control state in order to update the form field UI.
282-
this._control.stateChanges.pipe(startWith<void>(null!)).subscribe(() => {
277+
control.stateChanges.pipe(startWith<void>(null!)).subscribe(() => {
283278
this._validatePlaceholders();
284279
this._syncDescribedByIds();
285280
this._changeDetectorRef.markForCheck();
286281
});
287282

288-
// Run change detection if the value, prefix, or suffix changes.
289-
const valueChanges = this._control.ngControl && this._control.ngControl.valueChanges || EMPTY;
290-
merge(valueChanges, this._prefixChildren.changes, this._suffixChildren.changes)
291-
.subscribe(() => this._changeDetectorRef.markForCheck());
283+
// Run change detection if the value changes.
284+
if (control.ngControl && control.ngControl.valueChanges) {
285+
control.ngControl.valueChanges.subscribe(() => this._changeDetectorRef.markForCheck());
286+
}
287+
288+
// Run change detection and update the outline if the suffix or prefix changes.
289+
merge(this._prefixChildren.changes, this._suffixChildren.changes).subscribe(() => {
290+
this._updateOutlineGapOnStable();
291+
this._changeDetectorRef.markForCheck();
292+
});
292293

293294
// Re-validate when the number of hints changes.
294295
this._hintChildren.changes.pipe(startWith(null)).subscribe(() => {
@@ -503,4 +504,14 @@ export class MatFormField extends _MatFormFieldMixinBase
503504
private _getStartEnd(rect: ClientRect): number {
504505
return this._dir && this._dir.value === 'rtl' ? rect.right : rect.left;
505506
}
507+
508+
/** Updates the outline gap the new time the zone stabilizes. */
509+
private _updateOutlineGapOnStable() {
510+
// @breaking-change 7.0.0 Remove this check and else block once _ngZone is required.
511+
if (this._ngZone) {
512+
this._ngZone.onStable.pipe(take(1)).subscribe(() => this.updateOutlineGap());
513+
} else {
514+
Promise.resolve().then(() => this.updateOutlineGap());
515+
}
516+
}
506517
}

src/lib/input/input.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,28 @@ describe('MatInput with appearance', () => {
12811281
expect(parseInt(outlineGap.style.width)).toBeGreaterThan(0);
12821282
}));
12831283

1284+
it('should update the outline gap when the prefix/suffix is added or removed', fakeAsync(() => {
1285+
fixture.destroy();
1286+
TestBed.resetTestingModule();
1287+
1288+
const outlineFixture = createComponent(MatInputWithAppearanceAndLabel);
1289+
1290+
outlineFixture.componentInstance.appearance = 'outline';
1291+
outlineFixture.detectChanges();
1292+
flush();
1293+
outlineFixture.detectChanges();
1294+
1295+
spyOn(outlineFixture.componentInstance.formField, 'updateOutlineGap');
1296+
1297+
outlineFixture.componentInstance.showPrefix = true;
1298+
outlineFixture.detectChanges();
1299+
flush();
1300+
outlineFixture.detectChanges();
1301+
1302+
expect(outlineFixture.componentInstance.formField.updateOutlineGap).toHaveBeenCalled();
1303+
}));
1304+
1305+
12841306
});
12851307

12861308
describe('MatFormField default options', () => {
@@ -1735,13 +1757,16 @@ class MatInputWithAppearance {
17351757
@Component({
17361758
template: `
17371759
<mat-form-field [appearance]="appearance">
1760+
<span matPrefix *ngIf="showPrefix">Somewhat long prefix</span>
17381761
<mat-label>{{labelContent}}</mat-label>
17391762
<input matInput>
17401763
</mat-form-field>
17411764
`
17421765
})
17431766
class MatInputWithAppearanceAndLabel {
1767+
@ViewChild(MatFormField) formField: MatFormField;
17441768
appearance: MatFormFieldAppearance;
1769+
showPrefix: boolean;
17451770
labelContent = 'Label';
17461771
}
17471772

0 commit comments

Comments
 (0)