Skip to content

Commit de51165

Browse files
committed
fix(form-field): update label gap for outline style
1 parent 3a84259 commit de51165

File tree

4 files changed

+42
-43
lines changed

4 files changed

+42
-43
lines changed

src/lib/form-field/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ng_module(
1818
deps = [
1919
"//src/lib/core",
2020
"//src/cdk/coercion",
21+
"//src/cdk/observers",
2122
"//src/cdk/platform",
2223
],
2324
tsconfig = "//src/lib:tsconfig-build.json",

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import {CommonModule} from '@angular/common';
1010
import {NgModule} from '@angular/core';
11+
import {ObserversModule} from '@angular/cdk/observers';
1112
import {MatError} from './error';
1213
import {MatFormField} from './form-field';
1314
import {MatHint} from './hint';
@@ -27,7 +28,10 @@ import {MatSuffix} from './suffix';
2728
MatPrefix,
2829
MatSuffix,
2930
],
30-
imports: [CommonModule],
31+
imports: [
32+
CommonModule,
33+
ObserversModule,
34+
],
3135
exports: [
3236
MatError,
3337
MatFormField,

src/lib/form-field/form-field.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
<!-- Outline used for outline appearance. -->
66
<ng-container *ngIf="appearance == 'outline'">
77
<div class="mat-form-field-outline">
8-
<div class="mat-form-field-outline-start" [style.width.px]="_outlineGapStart"></div>
9-
<div class="mat-form-field-outline-gap" [style.width.px]="_outlineGapWidth"></div>
8+
<div class="mat-form-field-outline-start"></div>
9+
<div class="mat-form-field-outline-gap"></div>
1010
<div class="mat-form-field-outline-end"></div>
1111
</div>
1212
<div class="mat-form-field-outline mat-form-field-outline-thick">
13-
<div class="mat-form-field-outline-start" [style.width.px]="_outlineGapStart"></div>
14-
<div class="mat-form-field-outline-gap" [style.width.px]="_outlineGapWidth"></div>
13+
<div class="mat-form-field-outline-start"></div>
14+
<div class="mat-form-field-outline-gap"></div>
1515
<div class="mat-form-field-outline-end"></div>
1616
</div>
1717
</ng-container>
@@ -27,6 +27,7 @@
2727
<!-- We add aria-owns as a workaround for an issue in JAWS & NVDA where the label isn't
2828
read if it comes before the control in the DOM. -->
2929
<label class="mat-form-field-label"
30+
(cdkObserveContent)="updateOutlineGap()"
3031
[id]="_labelId"
3132
[attr.for]="_control.id"
3233
[attr.aria-owns]="_control.id"

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

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
Inject,
2222
InjectionToken,
2323
Input,
24-
NgZone,
2524
Optional,
2625
QueryList,
2726
ViewChild,
@@ -148,12 +147,13 @@ export class MatFormField extends _MatFormFieldMixinBase
148147
return this._appearance || this._defaultOptions && this._defaultOptions.appearance || 'legacy';
149148
}
150149
set appearance(value: MatFormFieldAppearance) {
151-
// If we're switching to `outline` from another appearance, we have to recalculate the gap.
152-
if (value !== this._appearance && value === 'outline') {
153-
this._initialGapCalculated = false;
154-
}
155-
156150
this._appearance = value;
151+
if (value === 'outline') {
152+
// The outline gap cannot update until change detection causes the ngIf updates, causing the
153+
// outline elements to show in the dom. This is a safe action to take because
154+
// updateOutlineGap does not cause a change detection cycle to run.
155+
Promise.resolve().then(() => this.updateOutlineGap());
156+
}
157157
}
158158
_appearance: MatFormFieldAppearance;
159159

@@ -217,17 +217,13 @@ export class MatFormField extends _MatFormFieldMixinBase
217217
/** Whether the Angular animations are enabled. */
218218
_animationsEnabled: boolean;
219219

220-
_outlineGapWidth = 0;
221-
_outlineGapStart = 0;
222-
_initialGapCalculated = false;
223-
224220
/**
225221
* @deprecated
226222
* @breaking-change 7.0.0
227223
*/
228224
@ViewChild('underline') underlineRef: ElementRef;
229225

230-
@ViewChild('connectionContainer') _connectionContainerRef: ElementRef;
226+
@ViewChild('connectionContainer') _connectionContainerRef: ElementRef<HTMLDivElement>;
231227
@ViewChild('inputContainer') _inputContainerRef: ElementRef;
232228
@ViewChild('label') private _label: ElementRef;
233229
@ContentChild(MatFormFieldControl) _control: MatFormFieldControl<any>;
@@ -245,9 +241,8 @@ export class MatFormField extends _MatFormFieldMixinBase
245241
@Optional() private _dir: Directionality,
246242
@Optional() @Inject(MAT_FORM_FIELD_DEFAULT_OPTIONS) private _defaultOptions:
247243
MatFormFieldDefaultOptions,
248-
// @breaking-change 7.0.0 _platform, _ngZone and _animationMode to be made required.
244+
// @breaking-change 7.0.0 _platform and _animationMode to be made required.
249245
private _platform?: Platform,
250-
private _ngZone?: NgZone,
251246
@Optional() @Inject(ANIMATION_MODULE_TYPE) _animationMode?: string) {
252247
super(_elementRef);
253248

@@ -298,26 +293,13 @@ export class MatFormField extends _MatFormFieldMixinBase
298293

299294
ngAfterContentChecked() {
300295
this._validateControlChild();
301-
302-
if (!this._initialGapCalculated) {
303-
// @breaking-change 7.0.0 Remove this check and else block once _ngZone is required.
304-
if (this._ngZone) {
305-
// It's important that we run this outside the `_ngZone`, because the `Promise.resolve`
306-
// can kick us into an infinite change detection loop, if the `_initialGapCalculated`
307-
// wasn't flipped on for some reason.
308-
this._ngZone.runOutsideAngular(() => {
309-
Promise.resolve().then(() => this.updateOutlineGap());
310-
});
311-
} else {
312-
Promise.resolve().then(() => this.updateOutlineGap());
313-
}
314-
}
315296
}
316297

317298
ngAfterViewInit() {
318299
// Avoid animations on load.
319300
this._subscriptAnimationState = 'enter';
320301
this._changeDetectorRef.detectChanges();
302+
this.updateOutlineGap();
321303
}
322304

323305
/** Determines whether a class from the NgControl should be forwarded to the host element. */
@@ -422,9 +404,9 @@ export class MatFormField extends _MatFormFieldMixinBase
422404
let ids: string[] = [];
423405

424406
if (this._getDisplayedMessages() === 'hint') {
425-
let startHint = this._hintChildren ?
407+
const startHint = this._hintChildren ?
426408
this._hintChildren.find(hint => hint.align === 'start') : null;
427-
let endHint = this._hintChildren ?
409+
const endHint = this._hintChildren ?
428410
this._hintChildren.find(hint => hint.align === 'end') : null;
429411

430412
if (startHint) {
@@ -456,10 +438,19 @@ export class MatFormField extends _MatFormFieldMixinBase
456438
* appearance.
457439
*/
458440
updateOutlineGap() {
459-
if (this.appearance === 'outline' && this._label && this._label.nativeElement.children.length) {
441+
if (this.appearance !== 'outline') {
442+
return;
443+
}
444+
445+
let startWidth = 0;
446+
let gapWidth = 0;
447+
const startEls = this._connectionContainerRef.nativeElement.querySelectorAll<HTMLDivElement>(
448+
'.mat-form-field-outline-start');
449+
const gapEls = this._connectionContainerRef.nativeElement.querySelectorAll<HTMLDivElement>(
450+
'.mat-form-field-outline-gap');
451+
if (this._label && this._label.nativeElement.children.length) {
460452
if (this._platform && !this._platform.isBrowser) {
461453
// getBoundingClientRect isn't available on the server.
462-
this._initialGapCalculated = true;
463454
return;
464455
}
465456
if (!document.documentElement.contains(this._elementRef.nativeElement)) {
@@ -474,14 +465,16 @@ export class MatFormField extends _MatFormFieldMixinBase
474465
for (const child of this._label.nativeElement.children) {
475466
labelWidth += child.offsetWidth;
476467
}
477-
this._outlineGapStart = labelStart - containerStart - outlineGapPadding;
478-
this._outlineGapWidth = labelWidth * floatingLabelScale + outlineGapPadding * 2;
479-
} else {
480-
this._outlineGapStart = 0;
481-
this._outlineGapWidth = 0;
468+
startWidth = labelStart - containerStart - outlineGapPadding;
469+
gapWidth = labelWidth * floatingLabelScale + outlineGapPadding * 2;
470+
}
471+
472+
for (let i = 0; i < startEls.length; i++) {
473+
startEls.item(i).style.width = `${startWidth}px`;
474+
}
475+
for (let i = 0; i < gapEls.length; i++) {
476+
gapEls.item(i).style.width = `${gapWidth}px`;
482477
}
483-
this._initialGapCalculated = true;
484-
this._changeDetectorRef.markForCheck();
485478
}
486479

487480
/** Gets the start end of the rect considering the current directionality. */

0 commit comments

Comments
 (0)