简体   繁体   中英

Angular reactive form is invalid on page load

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.


component.ts

  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
  }

template:


<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.

在此处输入图像描述

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 (ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM