Reputation: 867
I have a form where three of my inputs (two fields and 1 checkbox) are ng-invalid
when the page first loads. The checkbox is the one I'm most concerned about, because it appears with a red border ("this field is required") right off the bat. ng-invalid
as a class is setting that red border, while ng-valid
sets a grey border (which I want).
There's some other fields on the page but they're disabled and neither invalid nor valid.
What is setting the form fields as ng-invalid
? I'm suspecting that there's something wrong with how my FormGroup
and controls are set up, but I'm not sure what the culprit is. I noticed that ng-invalid
is being added to the FormGroup
itself, so to me solving that's the key to resolving this.
constructor(
private cdr: ChangeDetectorRef,
private formBuilder: FormBuilder
) { }
myFormGroup = this.formBuilder.group({
// other FormControls
password: new FormControl('', [Validators.required, Validators.minLength(MIN_PW)]),
confirmPassword: new FormControl('', Validators.required),
myCheckbox: new FormControl('', Validators.required)
}, { validator: PasswordValidation.MatchPassword }
);
ngOnInit(): void {
// etc
}
<form [formGroup]="myFormGroup" #myNgForm="ngForm" class="my-form">
<div>
<mat-form-field appearance="outline" class="password-field half-width">
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password">
<mat-error *ngIf="myFormGroup.controls['password'].invalid">Password is required and must contain at least 8 characters.</mat-error>
<button mat-icon-button matSuffix (click)="hidePassword = !hidePassword" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="hidePassword">
<mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-label>Password</mat-label>
<mat-hint>Must be at least 8 characters</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="ml-16 password-field half-width">
<input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="confirmPassword">
<mat-error *ngIf="myFormGroup.controls['confirmPassword'].pristine || myFormGroup.controls['confirmPassword'].errors?.MatchPassword">Passwords do not match</mat-error>
<button mat-icon-button matSuffix (click)="hidePassword = !hidePassword" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="hidePassword">
<mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-label>Confirm password</mat-label>
</mat-form-field>
</div>
<div>
<p class="full-width">
<mat-checkbox formControlName="myCheckbox" required>some text</mat-checkbox>
</p>
</div>
</form>
Here's the ng-invalid
class on the checkbox.
Upvotes: 1
Views: 7455
Reputation: 3171
What is setting the form fields as ng-invalid? I'm suspecting that there's something wrong with how my FormGroup and controls are set up, but I'm not sure what the culprit is. I noticed that ng-invalid is being added to the FormGroup itself, so to me solving that's the key to resolving this.
Since you have defined Validators such as required
, minLength
, etc and form controls are initialized with empty string, the validation fails and that's the reason for ng-invalid
class being added. This doesn't mean that the FormGroup and controls setup is wrong. Though you should have default initial value as false
instead of ''
for myCheckbox FormControl.
Apart from ng-invalid
class, Angular automatically adds other classes too, as can be seen in one of the screenshot in the question. You need to make use of them in combination with ng-invalid
class.
ng-pristine
implies that user hasn't modified the form control. Once user modifies the form control, the ng-dirty
class would be added.ng-untouched
implies that user hasn't interacted with the form control. Focus into the field and then simply move out (i.e blur) will add ng-touched
class since user has now interacted with the field.When any FormControl is invalid, the FormGroup would be invalid too. The above classes would be applied to FormGroup too. So if any FormControl has ng-dirty
or ng-touched
class, the same will apply to FormGroup too.
From docs which talks about importance of pristine state. Though it may be related to showing/hiding error message but applies to styling too:
Pristine means the user hasn't changed the value since it was displayed in this form. If you ignore the pristine state, you would hide the message only when the value is valid. If you arrive in this component with a new (blank) hero or an invalid hero, you'll see the error message immediately, before you've done anything.
You might want the message to display only when the user makes an invalid change. Hiding the message while the control is in the pristine state achieves that goal.
Upvotes: 1