Skip to content

Commit

Permalink
fix(material/form-field): trigger CD when form
Browse files Browse the repository at this point in the history
gets reassigned

fixes the issue we were not marking component
for changes when form is reassigned making it
not update UI for required asterisk

fixes angular#29066
  • Loading branch information
naaajii committed Feb 5, 2025
1 parent 6a48ffd commit 3fbbc82
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,15 @@ export class MatFormField
ngAfterContentChecked() {
this._assertFormFieldControl();

// if form field was being used with an input in first place and then replaced by other
// component such as select.
if (this._control !== this._previousControl) {
this._initializeControl(this._previousControl);
this._previousControl = this._control;
}

// mark for CD to make component catch up so control changes reflect
this._changeDetectorRef.markForCheck();
}

ngOnDestroy() {
Expand Down
68 changes: 68 additions & 0 deletions src/material/input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,48 @@ describe('MatMdcInput without forms', () => {
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
}));

it('should show the required star when FormControl is reassigned', fakeAsync(() => {
const fixture = createComponent(MatInputWithRequiredAssignableFormControl);
fixture.detectChanges();

// should have star by default
let label = fixture.debugElement.query(By.css('label'))!;
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();

fixture.componentInstance.reassignFormControl();
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();

// should be removed as form was reassigned with no required validators
label = fixture.debugElement.query(By.css('label'))!;
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeFalsy();
}));

it('should show the required star when required validator is toggled', fakeAsync(() => {
const fixture = createComponent(MatInputWithRequiredAssignableFormControl);
fixture.detectChanges();

// should have star by default
let label = fixture.debugElement.query(By.css('label'))!;
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();

fixture.componentInstance.removeRequiredValidtor();
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();

// should be removed as control validator was removed
label = fixture.debugElement.query(By.css('label'))!;
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeFalsy();

fixture.componentInstance.addRequiredValidator();
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();

// should contain star as control validator was readded
label = fixture.debugElement.query(By.css('label'))!;
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
}));

it('should not hide the required star if input is disabled', () => {
const fixture = createComponent(MatInputLabelRequiredTestComponent);

Expand Down Expand Up @@ -2324,3 +2366,29 @@ class MatInputSimple {}
standalone: false,
})
class InputWithNgContainerPrefixAndSuffix {}

@Component({
template: `
<mat-form-field>
<mat-label>Hello</mat-label>
<input matInput [formControl]="formControl">
</mat-form-field>`,
standalone: false,
})
class MatInputWithRequiredAssignableFormControl {
formControl = new FormControl('', [Validators.required]);

reassignFormControl() {
this.formControl = new FormControl();
}

addRequiredValidator() {
this.formControl.setValidators([Validators.required]);
this.formControl.updateValueAndValidity();
}

removeRequiredValidtor() {
this.formControl.setValidators([]);
this.formControl.updateValueAndValidity();
}
}

0 comments on commit 3fbbc82

Please sign in to comment.