Skip to content

Commit f112392

Browse files
committed
fix(material/form-field): update outline label offset on visibility and prefix content change
Fixes issues with the outlined form-field label offset not updating correctly when the form-field was attached to the DOM but not visible, or when the prefix container had dynamic content.
1 parent 88f6648 commit f112392

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

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

Lines changed: 46 additions & 6 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, Observable, Subject} from 'rxjs';
35+
import {map, switchMap, takeUntil} 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,
@@ -473,10 +478,16 @@ export class MatFormField
473478
* checking every change detection cycle.
474479
*/
475480
private _initializeOutlineLabelOffsetSubscriptions() {
476-
// Whenever the prefix changes, schedule an update of the label offset.
477-
this._prefixChildren.changes.subscribe(
478-
() => (this._needsOutlineLabelOffsetUpdateOnStable = true),
479-
);
481+
// Whenever the prefix changes, schedule an update for label offset.
482+
this._prefixChildren.changes.pipe(map(prefixes => !!prefixes.length)).subscribe(hasPrefix => {
483+
// If there are any prefix, schedule an update for their resize observer.
484+
// Otherwise, just schedule an update for label offset.
485+
if (hasPrefix) {
486+
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = true;
487+
} else {
488+
this._needsOutlineLabelOffsetUpdateOnStable = true;
489+
}
490+
});
480491

481492
// Note that we have to run outside of the `NgZone` explicitly, in order to avoid
482493
// throwing users into an infinite loop if `zone-patch-rxjs` is included.
@@ -487,6 +498,18 @@ export class MatFormField
487498
this._updateOutlineLabelOffset();
488499
}
489500
});
501+
502+
this._ngZone.onStable
503+
.pipe(
504+
switchMap(() => this._resizeObserver.observe(this._elementRef.nativeElement)),
505+
takeUntil(this._destroyed),
506+
)
507+
.subscribe(() => {
508+
if (this._needsOutlineLabelPrefixResizeObserverUpdateOnStable) {
509+
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = false;
510+
this._updateOutlineLabelOffsetOnPrefixResize();
511+
}
512+
});
490513
});
491514

492515
this._dir.change
@@ -672,6 +695,23 @@ export class MatFormField
672695
)`;
673696
}
674697

698+
/**
699+
* Updates the horizontal offset of the label in the outline appearance when the prefix containers are resized.
700+
*/
701+
private _updateOutlineLabelOffsetOnPrefixResize() {
702+
const iconPrefixContainer = this._iconPrefixContainer?.nativeElement;
703+
const textPrefixContainer = this._textPrefixContainer?.nativeElement;
704+
705+
merge(
706+
iconPrefixContainer ? this._resizeObserver.observe(iconPrefixContainer) : EMPTY,
707+
textPrefixContainer ? this._resizeObserver.observe(textPrefixContainer) : EMPTY,
708+
)
709+
.pipe(takeUntil(this._prefixChildren.changes))
710+
.subscribe(() => {
711+
this._updateOutlineLabelOffset();
712+
});
713+
}
714+
675715
/** Checks whether the form field is attached to the DOM. */
676716
private _isAttachedToDom(): boolean {
677717
const element: HTMLElement = this._elementRef.nativeElement;

0 commit comments

Comments
 (0)