Skip to content

fix(material/form-field): resolve memory leak #31643

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

Merged
merged 1 commit into from
Aug 4, 2025
Merged
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
13 changes: 9 additions & 4 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {_IdGenerator} from '@angular/cdk/a11y';
import {Directionality} from '@angular/cdk/bidi';
import {Direction, Directionality} from '@angular/cdk/bidi';
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
import {Platform} from '@angular/cdk/platform';
import {NgTemplateOutlet} from '@angular/common';
Expand All @@ -30,6 +30,7 @@ import {
afterRenderEffect,
computed,
contentChild,
effect,
inject,
signal,
viewChild,
Expand Down Expand Up @@ -190,13 +191,13 @@ export class MatFormField
{
_elementRef = inject(ElementRef);
private _changeDetectorRef = inject(ChangeDetectorRef);
private _dir = inject(Directionality);
private _platform = inject(Platform);
private _idGenerator = inject(_IdGenerator);
private _ngZone = inject(NgZone);
private _defaults = inject<MatFormFieldDefaultOptions>(MAT_FORM_FIELD_DEFAULT_OPTIONS, {
optional: true,
});
private _currentDirection: Direction;

@ViewChild('textField') _textField: ElementRef<HTMLElement>;
@ViewChild('iconPrefixContainer') _iconPrefixContainer: ElementRef<HTMLElement>;
Expand Down Expand Up @@ -346,6 +347,7 @@ export class MatFormField

constructor() {
const defaults = this._defaults;
const dir = inject(Directionality);

if (defaults) {
if (defaults.appearance) {
Expand All @@ -357,6 +359,10 @@ export class MatFormField
}
}

// We need this value inside a `afterRenderEffect`, however at the time of writing, reading the
// signal directly causes a memory leak (see https://github.com/angular/angular/issues/62980).
// TODO(crisbeto): clean this up once the framework issue is resolved.
effect(() => (this._currentDirection = dir.valueSignal()));
this._syncOutlineLabelOffset();
}

Expand Down Expand Up @@ -752,7 +758,6 @@ export class MatFormField
* incorporate the horizontal offset into their default text-field styles.
*/
private _getOutlinedLabelOffset(): OutlinedLabelStyles {
const dir = this._dir.valueSignal();
if (!this._hasOutline() || !this._floatingLabel) {
return null;
}
Expand All @@ -776,7 +781,7 @@ export class MatFormField
const textSuffixContainerWidth = textSuffixContainer?.getBoundingClientRect().width ?? 0;
// If the directionality is RTL, the x-axis transform needs to be inverted. This
// is because `transformX` does not change based on the page directionality.
const negate = dir === 'rtl' ? '-1' : '1';
const negate = this._currentDirection === 'rtl' ? '-1' : '1';
const prefixWidth = `${iconPrefixContainerWidth + textPrefixContainerWidth}px`;
const labelOffset = `var(--mat-mdc-form-field-label-offset-x, 0px)`;
const labelHorizontalOffset = `calc(${negate} * (${prefixWidth} + ${labelOffset}))`;
Expand Down
Loading