Angular Material Form Validation

29,968

The form itself can be checked with valid and invalid. The below should work:

[disabled]="biodataForm.invalid"

Info on the Angular form group can be found at: https://angular.io/api/forms/FormGroup

In addition, the email and phone inputs...you are using [(value)]...change those to [(ngModel)] and it should work the way you expect.

Share:
29,968
vicatcu
Author by

vicatcu

Expertise in Embedded Systems / Microcontrollers Cornell University B.S. ECE '02, M.Eng ECE '04 Co-Founder of Wicked Device, LLC Air Quality Egg Zynect Sensors

Updated on November 23, 2020

Comments

  • vicatcu
    vicatcu over 3 years

    If the fields of my FormGroup are model-bound, ala [(ngModel)], and become populated on page load, e.g. because of a service, my Submit Button, which is guarded as [disabled]="biodataForm.status !== 'VALID'", does not become disabled. If the form comes up blank and I fill it in normally, the guard passes when the form is correctly filled out. If the same exact values are populated through the data-binding, the biodataForm.status value remains INVALID until I change the value of every field.

    My feeling is that the form should recognize it has valid content after the data-bindings are populated, and its status should change from INVALID to VALID as a result... what's going wrong here?

    My form markup looks like this:

      <form class="form" name="biodataForm" [formGroup]="biodataForm">
        <mat-form-field class="full-width">
          <input matInput placeholder="First Name" 
            required
            [(ngModel)]="_memberdata.firstname"
            [formControl]="firstnameFormControl">
          <mat-error *ngIf="firstnameFormControl.invalid">{{getRequiredErrorMessage('firstname')}}</mat-error>        
        </mat-form-field>
        <mat-form-field class="full-width">
          <input matInput placeholder="Last Name" 
            required
            [(ngModel)]="_memberdata.lastname"
            [formControl]="lastnameFormControl">
          <mat-error *ngIf="lastnameFormControl.invalid">{{getRequiredErrorMessage('lastname')}}</mat-error>                
        </mat-form-field>
        <mat-form-field class="full-width"
          hintLabel="Note: We'll send you an email with a link to click to prove it's you">
          <input matInput placeholder="Email" 
            required
            [(value)]="_memberdata.email"
            [formControl]="emailFormControl">
          <mat-error *ngIf="emailFormControl.invalid">{{getEmailErrorMessage()}}</mat-error>        
        </mat-form-field>    
        <mat-form-field class="full-width">
          <input matInput placeholder="Phone" type="tel" 
            [(value)]="_memberdata.phone"
            required
            [formControl]="phoneFormControl">
          <mat-error *ngIf="phoneFormControl.invalid">{{getPhoneErrorMessage()}}</mat-error>
        </mat-form-field>        
        <button mat-raised-button color="primary" 
          class="submit-button"
          [disabled]="biodataForm.status !== 'VALID'"
          (click)="handleNext()">Next Step</button>
      </form>
    

    ```

    My Angular component surrounding this form looks like this (details omitted for clarity, full source is here):

    export class WelcomeComponent {
      emailFormControl = new FormControl('', [
        Validators.required,
        Validators.email,
      ]);
      firstnameFormControl = new FormControl('', [Validators.required]);
      lastnameFormControl = new FormControl('', [Validators.required]);
      phoneFormControl = new FormControl('', [
        Validators.required,
        Validators.pattern(/(\(?[0-9]{3}\)?-?\s?[0-9]{3}-?[0-9]{4})/)
      ]);
      // addressFormControl = new FormControl('', [Validators.required]);
      biodataForm: FormGroup = new FormGroup({
        email: this.emailFormControl,
        firstname: this.firstnameFormControl,
        lastname: this.lastnameFormControl,
        phone: this.phoneFormControl
        // address: this.addressFormControl
      });
    
      getEmailErrorMessage() {
        return this.emailFormControl.hasError('required') ? 'You must enter a value' :
          this.emailFormControl.hasError('email') ? 'Not a valid email' : '';
      }
      getPhoneErrorMessage() {
        return this.phoneFormControl.hasError('required') ? 'You must enter a value' :
          this.phoneFormControl.hasError('pattern') ? 'Format must be (xxx) xxx-xxxx' : '';
      }
      getRequiredErrorMessage(field) {
        return this.biodataForm.get(field).hasError('required') ? 'You must enter a value' : '';
      }
    
      constructor(
        public _memberdata: MemberDataService,
        private _api: ApiService,
        private _router: Router,
        private _snackBar: MatSnackBar) { }
    
      handleNext() {
         // ... handle button click
      }
    }
    

    ```

  • vicatcu
    vicatcu about 6 years
    Unfortunately, just tried this, behaves the same way as my posted code (re: not identifying 'valid' state with preloaded model-bound fields). Live development version is running at ithacagenerator.org/onboard if you want to have a look
  • jdgower
    jdgower about 6 years
    Edited my answer...that should do it.
  • vicatcu
    vicatcu about 6 years
    You rock, thanks so much. That was bone-headed, thanks for the fresh eyes.