Skip to content

Commit

Permalink
fix(material/input): show required asterisk when using required valid…
Browse files Browse the repository at this point in the history
…ator

Resolves the long-standing issue where the required asterisk wasn't being shown in the form field label when using the `required` validator from Forms.

Fixes angular#2574.
  • Loading branch information
crisbeto committed Aug 13, 2021
1 parent bcb16ab commit 4b588d1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 10 deletions.
31 changes: 28 additions & 3 deletions src/material-experimental/mdc-input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ describe('MatMdcInput without forms', () => {
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
}));

it('should show the required star when using a FormControl', fakeAsync(() => {
const fixture = createComponent(MatInputWithRequiredFormControl);
fixture.detectChanges();

const label = fixture.debugElement.query(By.css('label'))!;
expect(label).not.toBeNull();
expect(label.nativeElement.textContent).toBe('Hello');
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
}));

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

Expand Down Expand Up @@ -1058,7 +1068,7 @@ describe('MatMdcInput with forms', () => {

expect(testComponent.formControl.invalid)
.withContext('Expected form control to be invalid').toBe(true);
expect(inputEl.value).toBeFalsy();
expect(inputEl.value).toBe('incorrect');
expect(inputEl.getAttribute('aria-invalid'))
.withContext('Expected aria-invalid to be set to "true"').toBe('true');

Expand Down Expand Up @@ -1548,7 +1558,10 @@ class MatInputMissingMatInputTestController {}
})
class MatInputWithFormErrorMessages {
@ViewChild('form') form: NgForm;
formControl = new FormControl('', [Validators.required, Validators.pattern(/valid value/)]);
formControl = new FormControl('incorrect', [
Validators.required,
Validators.pattern(/valid value/)
]);
renderError = true;
}

Expand Down Expand Up @@ -1591,7 +1604,7 @@ class MatInputWithCustomErrorStateMatcher {
class MatInputWithFormGroupErrorMessages {
@ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;
formGroup = new FormGroup({
name: new FormControl('', [Validators.required, Validators.pattern(/valid value/)])
name: new FormControl('incorrect', [Validators.required, Validators.pattern(/valid value/)])
});
}

Expand Down Expand Up @@ -1790,3 +1803,15 @@ class MatInputWithColor {
`
})
class MatInputInsideOutsideFormField {}


@Component({
template: `
<mat-form-field>
<mat-label>Hello</mat-label>
<input matInput [formControl]="formControl">
</mat-form-field>`
})
class MatInputWithRequiredFormControl {
formControl = new FormControl('', [Validators.required]);
}
29 changes: 26 additions & 3 deletions src/material/input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,15 @@ describe('MatInput without forms', () => {
expect(labelEl.nativeElement.textContent).toBe('hello *');
}));

it('should show the required star when using a FormControl', fakeAsync(() => {
const fixture = createComponent(MatInputWithRequiredFormControl);
fixture.detectChanges();

const labelEl = fixture.debugElement.query(By.css('label'))!;
expect(labelEl).not.toBeNull();
expect(labelEl.nativeElement.textContent).toBe('Hello *');
}));

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

Expand Down Expand Up @@ -1203,7 +1212,7 @@ describe('MatInput with forms', () => {

expect(testComponent.formControl.invalid)
.withContext('Expected form control to be invalid').toBe(true);
expect(inputEl.value).toBeFalsy();
expect(inputEl.value).toBe('incorrect');
expect(inputEl.getAttribute('aria-invalid'))
.withContext('Expected aria-invalid to be set to "true".').toBe('true');

Expand Down Expand Up @@ -2018,7 +2027,10 @@ class MatInputMissingMatInputTestController {}
})
class MatInputWithFormErrorMessages {
@ViewChild('form') form: NgForm;
formControl = new FormControl('', [Validators.required, Validators.pattern(/valid value/)]);
formControl = new FormControl('incorrect', [
Validators.required,
Validators.pattern(/valid value/)
]);
renderError = true;
}

Expand Down Expand Up @@ -2061,7 +2073,7 @@ class MatInputWithCustomErrorStateMatcher {
class MatInputWithFormGroupErrorMessages {
@ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;
formGroup = new FormGroup({
name: new FormControl('', [Validators.required, Validators.pattern(/valid value/)])
name: new FormControl('incorrect', [Validators.required, Validators.pattern(/valid value/)])
});
}

Expand Down Expand Up @@ -2360,3 +2372,14 @@ class MatInputWithAnotherNgIf {
class MatInputWithColor {
color: ThemePalette;
}


@Component({
template: `
<mat-form-field>
<input matInput placeholder="Hello" [formControl]="formControl">
</mat-form-field>`
})
class MatInputWithRequiredFormControl {
formControl = new FormControl('', [Validators.required]);
}
8 changes: 5 additions & 3 deletions src/material/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
Optional,
Self,
} from '@angular/core';
import {FormGroupDirective, NgControl, NgForm} from '@angular/forms';
import {FormGroupDirective, NgControl, NgForm, Validators} from '@angular/forms';
import {
CanUpdateErrorState,
ErrorStateMatcher,
Expand Down Expand Up @@ -174,9 +174,11 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
* @docs-private
*/
@Input()
get required(): boolean { return this._required; }
get required(): boolean {
return this._required ?? this.ngControl?.control?.hasValidator(Validators.required) ?? false;
}
set required(value: boolean) { this._required = coerceBooleanProperty(value); }
protected _required = false;
protected _required: boolean | undefined;

/** Input type of the element. */
@Input()
Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/material/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class MatInput extends _MatInputBase implements MatFormFieldControl<any>,
get required(): boolean;
set required(value: boolean);
// (undocumented)
protected _required: boolean;
protected _required: boolean | undefined;
setDescribedByIds(ids: string[]): void;
get shouldLabelFloat(): boolean;
readonly stateChanges: Subject<void>;
Expand Down

0 comments on commit 4b588d1

Please sign in to comment.