Skip to content

Commit 0060bd7

Browse files
crisbetovivian-hu-zz
authored andcommitted
fix(form-field): not updating outline when prefix/suffix is added or removed (#13253)
Fixes the form field's outline not reacting to the prefix/suffix being toggled after init. Fixes #13251.
1 parent 1c60acf commit 0060bd7

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';
@@ -154,14 +154,7 @@ export class MatFormField extends _MatFormFieldMixinBase
154154
this._appearance = value || (this._defaults && this._defaults.appearance) || 'legacy';
155155

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

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

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

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

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

src/lib/input/input.spec.ts

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

1322+
it('should update the outline gap when the prefix/suffix is added or removed', fakeAsync(() => {
1323+
fixture.destroy();
1324+
TestBed.resetTestingModule();
1325+
1326+
const outlineFixture = createComponent(MatInputWithAppearanceAndLabel);
1327+
1328+
outlineFixture.componentInstance.appearance = 'outline';
1329+
outlineFixture.detectChanges();
1330+
flush();
1331+
outlineFixture.detectChanges();
1332+
1333+
spyOn(outlineFixture.componentInstance.formField, 'updateOutlineGap');
1334+
1335+
outlineFixture.componentInstance.showPrefix = true;
1336+
outlineFixture.detectChanges();
1337+
flush();
1338+
outlineFixture.detectChanges();
1339+
1340+
expect(outlineFixture.componentInstance.formField.updateOutlineGap).toHaveBeenCalled();
1341+
}));
1342+
1343+
13221344
});
13231345

13241346
describe('MatFormField default options', () => {
@@ -1774,13 +1796,16 @@ class MatInputWithAppearance {
17741796
@Component({
17751797
template: `
17761798
<mat-form-field [appearance]="appearance">
1799+
<span matPrefix *ngIf="showPrefix">Somewhat long prefix</span>
17771800
<mat-label>{{labelContent}}</mat-label>
17781801
<input matInput>
17791802
</mat-form-field>
17801803
`
17811804
})
17821805
class MatInputWithAppearanceAndLabel {
1806+
@ViewChild(MatFormField) formField: MatFormField;
17831807
appearance: MatFormFieldAppearance;
1808+
showPrefix: boolean;
17841809
labelContent = 'Label';
17851810
}
17861811

0 commit comments

Comments
 (0)