Skip to content

Commit 932f0a7

Browse files
committed
fix(material/form-field): position outlined form field label correctly when form-field is invisible.
Fixes outlined form-field label position, in cases where form field is attached to the DOM but it's invisible.
1 parent 88f6648 commit 932f0a7

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

src/material/form-field/form-field.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
ContentChild,
1818
ContentChildren,
1919
ElementRef,
20+
inject,
2021
Inject,
2122
InjectionToken,
2223
Input,
@@ -30,8 +31,8 @@ import {
3031
import {AbstractControlDirective} from '@angular/forms';
3132
import {ThemePalette} from '@angular/material/core';
3233
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
33-
import {merge, Subject} from 'rxjs';
34-
import {takeUntil} from 'rxjs/operators';
34+
import {EMPTY, merge, Subject} from 'rxjs';
35+
import {finalize, switchMap, takeUntil, tap} from 'rxjs/operators';
3536
import {MAT_ERROR, MatError} from './directives/error';
3637
import {
3738
FLOATING_LABEL_PARENT,
@@ -52,6 +53,7 @@ import {
5253
getMatFormFieldMissingControlError,
5354
} from './form-field-errors';
5455
import {DOCUMENT} from '@angular/common';
56+
import {SharedResizeObserver} from '@angular/cdk/observers/private';
5557

5658
/** Type for the available floatLabel values. */
5759
export type FloatLabelType = 'always' | 'auto';
@@ -284,6 +286,9 @@ export class MatFormField
284286
private _isFocused: boolean | null = null;
285287
private _explicitFormFieldControl: MatFormFieldControl<any>;
286288
private _needsOutlineLabelOffsetUpdateOnStable = false;
289+
private _needsOutlineLabelPrefixResizeObserverUpdateOnStable = true;
290+
291+
private _resizeObserver = inject(SharedResizeObserver);
287292

288293
constructor(
289294
public _elementRef: ElementRef,
@@ -474,9 +479,9 @@ export class MatFormField
474479
*/
475480
private _initializeOutlineLabelOffsetSubscriptions() {
476481
// Whenever the prefix changes, schedule an update of the label offset.
477-
this._prefixChildren.changes.subscribe(
478-
() => (this._needsOutlineLabelOffsetUpdateOnStable = true),
479-
);
482+
this._prefixChildren.changes.subscribe(() => {
483+
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = true;
484+
});
480485

481486
// Note that we have to run outside of the `NgZone` explicitly, in order to avoid
482487
// throwing users into an infinite loop if `zone-patch-rxjs` is included.
@@ -487,6 +492,18 @@ export class MatFormField
487492
this._updateOutlineLabelOffset();
488493
}
489494
});
495+
496+
this._ngZone.onStable
497+
.pipe(
498+
takeUntil(this._destroyed),
499+
switchMap(() => this._resizeObserver.observe(this._elementRef.nativeElement)),
500+
)
501+
.subscribe(() => {
502+
if (this._needsOutlineLabelPrefixResizeObserverUpdateOnStable) {
503+
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = false;
504+
this._updateOutlineLabelOffsetOnPrefixResize();
505+
}
506+
});
490507
});
491508

492509
this._dir.change
@@ -646,8 +663,8 @@ export class MatFormField
646663
floatingLabel.style.transform = '';
647664
return;
648665
}
649-
// If the form field is not attached to the DOM yet (e.g. in a tab), we defer
650-
// the label offset update until the zone stabilizes.
666+
// If the form field is invisible (e.g. in a drawer) or not attached to the DOM yet (e.g. in a tab)),
667+
// we defer the label offset update until the zone stabilizes.
651668
if (!this._isAttachedToDom()) {
652669
this._needsOutlineLabelOffsetUpdateOnStable = true;
653670
return;
@@ -672,6 +689,23 @@ export class MatFormField
672689
)`;
673690
}
674691

692+
/**
693+
* Updates the horizontal offset of the label in the outline appearance when the prefix containers are resized.
694+
*/
695+
private _updateOutlineLabelOffsetOnPrefixResize() {
696+
const iconPrefixContainer = this._iconPrefixContainer?.nativeElement;
697+
const textPrefixContainer = this._textPrefixContainer?.nativeElement;
698+
699+
merge(
700+
iconPrefixContainer ? this._resizeObserver.observe(iconPrefixContainer) : EMPTY,
701+
textPrefixContainer ? this._resizeObserver.observe(textPrefixContainer) : EMPTY,
702+
)
703+
.pipe(takeUntil(this._prefixChildren.changes))
704+
.subscribe(() => {
705+
this._updateOutlineLabelOffset();
706+
});
707+
}
708+
675709
/** Checks whether the form field is attached to the DOM. */
676710
private _isAttachedToDom(): boolean {
677711
const element: HTMLElement = this._elementRef.nativeElement;

0 commit comments

Comments
 (0)