Skip to content

fix(material/form-field): position outlined form field label correctl… #27525

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ContentChild,
ContentChildren,
ElementRef,
inject,
Inject,
InjectionToken,
Input,
Expand All @@ -30,8 +31,8 @@ import {
import {AbstractControlDirective} from '@angular/forms';
import {ThemePalette} from '@angular/material/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
import {merge, Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {EMPTY, merge, Subject} from 'rxjs';
import {map, switchMap, takeUntil} from 'rxjs/operators';
import {MAT_ERROR, MatError} from './directives/error';
import {
FLOATING_LABEL_PARENT,
Expand All @@ -52,6 +53,7 @@ import {
getMatFormFieldMissingControlError,
} from './form-field-errors';
import {DOCUMENT} from '@angular/common';
import {SharedResizeObserver} from '@angular/cdk/observers/private';

/** Type for the available floatLabel values. */
export type FloatLabelType = 'always' | 'auto';
Expand Down Expand Up @@ -284,6 +286,9 @@ export class MatFormField
private _isFocused: boolean | null = null;
private _explicitFormFieldControl: MatFormFieldControl<any>;
private _needsOutlineLabelOffsetUpdateOnStable = false;
private _needsOutlineLabelPrefixResizeObserverUpdateOnStable = true;

private _resizeObserver = inject(SharedResizeObserver);

constructor(
public _elementRef: ElementRef,
Expand Down Expand Up @@ -473,10 +478,16 @@ export class MatFormField
* checking every change detection cycle.
*/
private _initializeOutlineLabelOffsetSubscriptions() {
// Whenever the prefix changes, schedule an update of the label offset.
this._prefixChildren.changes.subscribe(
() => (this._needsOutlineLabelOffsetUpdateOnStable = true),
);
// Whenever the prefix changes, schedule an update for label offset.
this._prefixChildren.changes.pipe(map(prefixes => !!prefixes.length)).subscribe(hasPrefix => {
// If there are any prefix, schedule an update for their resize observer.
// Otherwise, just schedule an update for label offset.
if (hasPrefix) {
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = true;
} else {
this._needsOutlineLabelOffsetUpdateOnStable = true;
}
});

// Note that we have to run outside of the `NgZone` explicitly, in order to avoid
// throwing users into an infinite loop if `zone-patch-rxjs` is included.
Expand All @@ -487,6 +498,18 @@ export class MatFormField
this._updateOutlineLabelOffset();
}
});

this._ngZone.onStable
.pipe(
switchMap(() => this._resizeObserver.observe(this._elementRef.nativeElement)),
takeUntil(this._destroyed),
)
.subscribe(() => {
if (this._needsOutlineLabelPrefixResizeObserverUpdateOnStable) {
this._needsOutlineLabelPrefixResizeObserverUpdateOnStable = false;
this._updateOutlineLabelOffsetOnPrefixResize();
}
});
});

this._dir.change
Expand Down Expand Up @@ -672,6 +695,23 @@ export class MatFormField
)`;
}

/**
* Updates the horizontal offset of the label in the outline appearance when the prefix containers are resized.
*/
private _updateOutlineLabelOffsetOnPrefixResize() {
const iconPrefixContainer = this._iconPrefixContainer?.nativeElement;
const textPrefixContainer = this._textPrefixContainer?.nativeElement;

merge(
iconPrefixContainer ? this._resizeObserver.observe(iconPrefixContainer) : EMPTY,
textPrefixContainer ? this._resizeObserver.observe(textPrefixContainer) : EMPTY,
)
.pipe(takeUntil(this._prefixChildren.changes))
.subscribe(() => {
this._updateOutlineLabelOffset();
});
}

/** Checks whether the form field is attached to the DOM. */
private _isAttachedToDom(): boolean {
const element: HTMLElement = this._elementRef.nativeElement;
Expand Down