diff --git a/src/material/form-field/form-field.ts b/src/material/form-field/form-field.ts index 914f89a32d2e..1a767b2de6ff 100644 --- a/src/material/form-field/form-field.ts +++ b/src/material/form-field/form-field.ts @@ -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 { @@ -318,6 +318,9 @@ export class MatFormField private _isFocused: boolean | null = null; private _explicitFormFieldControl: MatFormFieldControl; private _needsOutlineLabelOffsetUpdate = false; + private _previousControl: MatFormFieldControl | null = null; + private _stateChanges: Subscription | undefined; + private _valueChanges: Subscription | undefined; private _injector = inject(Injector); @@ -365,7 +368,6 @@ export class MatFormField ngAfterContentInit() { this._assertFormFieldControl(); - this._initializeControl(); this._initializeSubscript(); this._initializePrefixAndSuffix(); this._initializeOutlineLabelOffsetSubscriptions(); @@ -373,9 +375,16 @@ export class MatFormField 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(); } @@ -409,25 +418,31 @@ export class MatFormField } /** Initializes the registered form field control. */ - private _initializeControl() { + private _initializeControl(previousControl: MatFormFieldControl | 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()); }