Skip to content

Commit

Permalink
fix(material/form-field): update state if control changes (#29573)
Browse files Browse the repository at this point in the history
Fixes that the form field wasn't accounting for the case where the form control is swapped out.

Fixes #29402.
  • Loading branch information
crisbeto authored Aug 13, 2024
1 parent c3e0279 commit ec35e99
Showing 1 changed file with 23 additions and 8 deletions.
31 changes: 23 additions & 8 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
} from '@angular/core';
import {AbstractControlDirective} from '@angular/forms';
import {ThemePalette} from '@angular/material/core';
import {Subject, merge} from 'rxjs';
import {Subject, Subscription, merge} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {MAT_ERROR, MatError} from './directives/error';
import {
Expand Down Expand Up @@ -318,6 +318,9 @@ export class MatFormField
private _isFocused: boolean | null = null;
private _explicitFormFieldControl: MatFormFieldControl<any>;
private _needsOutlineLabelOffsetUpdate = false;
private _previousControl: MatFormFieldControl<unknown> | null = null;
private _stateChanges: Subscription | undefined;
private _valueChanges: Subscription | undefined;

private _injector = inject(Injector);

Expand Down Expand Up @@ -365,17 +368,23 @@ export class MatFormField

ngAfterContentInit() {
this._assertFormFieldControl();
this._initializeControl();
this._initializeSubscript();
this._initializePrefixAndSuffix();
this._initializeOutlineLabelOffsetSubscriptions();
}

ngAfterContentChecked() {
this._assertFormFieldControl();

if (this._control !== this._previousControl) {
this._initializeControl(this._previousControl);
this._previousControl = this._control;
}
}

ngOnDestroy() {
this._stateChanges?.unsubscribe();
this._valueChanges?.unsubscribe();
this._destroyed.next();
this._destroyed.complete();
}
Expand Down Expand Up @@ -409,25 +418,31 @@ export class MatFormField
}

/** Initializes the registered form field control. */
private _initializeControl() {
private _initializeControl(previousControl: MatFormFieldControl<unknown> | null) {
const control = this._control;
const classPrefix = 'mat-mdc-form-field-type-';

if (previousControl) {
this._elementRef.nativeElement.classList.remove(classPrefix + previousControl.controlType);
}

if (control.controlType) {
this._elementRef.nativeElement.classList.add(
`mat-mdc-form-field-type-${control.controlType}`,
);
this._elementRef.nativeElement.classList.add(classPrefix + control.controlType);
}

// Subscribe to changes in the child control state in order to update the form field UI.
control.stateChanges.subscribe(() => {
this._stateChanges?.unsubscribe();
this._stateChanges = control.stateChanges.subscribe(() => {
this._updateFocusState();
this._syncDescribedByIds();
this._changeDetectorRef.markForCheck();
});

this._valueChanges?.unsubscribe();

// Run change detection if the value changes.
if (control.ngControl && control.ngControl.valueChanges) {
control.ngControl.valueChanges
this._valueChanges = control.ngControl.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(() => this._changeDetectorRef.markForCheck());
}
Expand Down

0 comments on commit ec35e99

Please sign in to comment.