-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
fix(material/form-field): update state if control changes #29573
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<any>; | ||
private _needsOutlineLabelOffsetUpdate = false; | ||
private _previousControl: MatFormFieldControl<unknown> | null = null; | ||
private _stateChanges: Subscription | undefined; | ||
private _valueChanges: Subscription | undefined; | ||
|
||
private _injector = inject(Injector); | ||
|
||
|
@@ -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(); | ||
} | ||
|
@@ -409,25 +418,31 @@ export class MatFormField | |
} | ||
|
||
/** Initializes the registered form field control. */ | ||
private _initializeControl() { | ||
private _initializeControl(previousControl: MatFormFieldControl<unknown> | null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why pass previousControl in, can't we just read it off the class like we do for control? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to keep the code that re-assigns it in the |
||
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()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option for this would've been to use signals, but it seems like
_control
is set up as some sort of backwards compatibility layer so I didn't want to change it into a computed which would've been breaking.