Javascript required
Skip to content Skip to sidebar Skip to footer

Angular Reactive Forms Cannot Read Property 'invalid' of Undefined

  • Learning Objectives
  • Validators
  • Form Control State
    • Dirty & Pristine
    • Touched & Untouched
    • Valid & Invalid
  • Validation Styling
    • Writing Shorter Validation Expressions
  • Validation Letters
  • Summary
  • Listing

Learning Objectives

  • How to add validation checks to our class via the class model.

  • How tostyle our form in social club to give visual feedback to the user then they know when the fields don't pass the validation checks.

  • How to add validation error messages to the class to give hints to the user about why the field isn't passing a validation check.

Validators

Carrying on from the model-driven form we started in the previous lecture.

Our form is valid all the time, regardless of what input the user types into the controls.

Validators are rules which an input control has to follow. If the input doesn't friction match the rule and so the control is said to be invalid.

Since it's a signup form most of the fields should exist required and I would want to specify some more complex validators on the password field to brand sure the user is entering a good strong password.

We can apply validators either past calculation attributes to the template or by defining them on our FormControls in our model.

To stick to the theme of being model-driven we are going to add validators to the form model direct.

Angular comes with a small set of pre-congenital validators to lucifer the ones nosotros can define via standard HTMLfive attributes, namely required, minlegth, maxlength andpattern which nosotros can access from theValidators module.

The start parameter of a FormControl constructor is the initial value of the command, we'll exit that as empty string. The 2d parameter contains either a unmarried validator if we only want to utilise one, or a list of validators if we desire to use multiple validators to a unmarried command.

Our model then looks something like this:

                import { FormGroup, FormControl, Validators } from '@athwart/forms'; . . . class ModelFormComponent implements OnInit {   myform: FormGroup;    ngOnInit() {     myform = new FormGroup({         proper noun: new FormGroup({             firstName: new FormControl('', Validators.required),                                    (1)                  lastName: new FormControl('', Validators.required),         }),         email: new FormControl('', [                                    (2)                  Validators.required,             Validators.pattern("[^ @]*@[^ @]*")                                    (three)                  ]),         countersign: new FormControl('', [             Validators.minLength(8),                                    (4)                  Validators.required         ]),         linguistic communication: new FormControl()                                    (5)                  });   } }              
ane We add a single required validator to marking this control as required.
2 We can as well provide an array of validators.
3 We specify a blueprint validator which checks whether the email contains a@ graphic symbol.
4 Theminlength validator checks to see if the countersign is a minimum of 8 characters long.
5 We don't add any validators to the language select box.

Form Control State

The form control instance on our model encapsulates country near the control itself, such as if it is currently valid or if information technology'southward been touched.

Muddy & Pristine

We can become a reference to these form control instances in our template through thecontrols property of our myform model, for instance we can print out the the muddy state of the electronic mail field like so:

                  <pre>Dirty? {{ myform.controls.email.muddied }}</pre>                

dirty is true if the user has inverse the value of the control.

The opposite ofdirty is pristine so if we wrote:

                  <pre>Pristine? {{ myform.controls.e-mail.pristine }}</pre>                

This would exist true if the user hasn't changed the value, andimitation if the user has.

Touched & Untouched

A controls is said to be touched if the the user focused on the control and and then focused on something else. For example by clicking into the control and then pressing tab or clicking on another command in the form.

The difference between touched anddirty is that with touched the user doesn't demand to really change the value of the input control.

                  <pre>Touched? {{ myform.controls.e-mail.touched }}</pre>                

touched is true of the field has been touched past the user, otherwise it'southward fake.

The opposite oftouched is the holding untouched.

Valid & Invalid

We tin can besides check thevalid land of the control with:

                  <pre>Valid? {{ myform.controls.email.valid }}</pre>                

valid is true of the field doesn't have whatever validators or if all the validators are passing.

Once more the opposite ofvalid is invalid, so we could write:

                  <pre>Invalid? {{ myform.controls.email.invalid }}</pre>                

This would be true if the command was invalid andfalse if information technology was valid.

Validation Styling

Bootstrap has classes for showing visual feedback for course controls when they are invalid.

For instance if we add thehas-danger form to the parent div of the input command with the class ofform-grouping it adds ared border.

Conversely if we add thehas-success class it adds agreen border.

Form Valid

Figure 1. Valid Form Control

Form Invalid

Figure 2. Invalid Course Control

We can combine Bootstrap classes with dirty andinvalid FormControl properties and the ngClass directive to give the user some nice visual feedback, like and so:

                <div class="class-grouping" [ngClass]="{   'has-danger': myform.controls.electronic mail.invalid && myform.controls.email.dirty,                                    (1)                  'has-success': myform.controls.email.valid && myform.controls.electronic mail.dirty                                    (2)                  }">              
1 If the email is invalid and it'due south been touched by the user then we add thehas-danger class giving the control a red border.
2 If the email is valid and it's been touched past the user and so nosotros add thehas-success course giving the control a green border.

Tip

The reason we check for the dirty property existence true is then nosotros don't show the user visual feedback when the form is first displayed. Instead we only show the user feedback when they have had a chance to edit the field.

Now the input control shows thegreenish edge when it's valid anddirty andred if it's invalid anddingy.

Writing Shorter Validation Expressions

The higher up can quickly become cumbersome to use in our templates, especially for things like the nested firstName andlastName controls.

Since thefirstName andlastName FormControls exist nether thename FormGroup to access those from the template we demand to use syntax like this:

                  <div class="form-group"        [ngClass]="{         'has-danger': myform.controls.proper name.controls.firstName.invalid && myform.controls.name.controls.firstName.dingy,         'has-success': myform.controls.proper noun.controls.firstName.valid && myform.controls.proper noun.controls.firstName.dirty }">                

The length of the expression quickly becomes unwieldy.

We can help ourselves here past creating local backdrop on our component to reverberate the private FormControls and bounden directly to them in our template, like so:

Listing 1. script.ts

                    class ModelFormComponent implements OnInit {   langs: string[] = [     'English',     'French',     'German',   ];   myform: FormGroup;   firstName: FormControl;                                            (ane)                      lastName: FormControl;   email: FormControl;   password: FormControl;   language: FormControl;    ngOnInit() {     this.createFormControls();     this.createForm();   }    createFormControls() {                                            (ii)                      this.firstName = new FormControl('', Validators.required);     this.lastName = new FormControl('', Validators.required);     this.email = new FormControl('', [       Validators.required,       Validators.pattern("[^ @]*@[^ @]*")     ]);     this.password = new FormControl('', [       Validators.required,       Validators.minLength(8)     ]);     this.language = new FormControl('', Validators.required);   }    createForm() {                                            (iii)                      this.myform = new FormGroup({       name: new FormGroup({         firstName: this.firstName,         lastName: this.lastName,       }),       email: this.electronic mail,       password: this.password,       language: this.language     });   } }                  
ane We declare theFormControls equally backdrop of our component. And then we tin can bind to them directly in our tempalte without having to go through the superlative-level myform model.
two We first create theFormControls.
3 Nosotros and then construct themyform model from the form controls we created previously and stored as properties on our component.

At present we tin can bind directly to our individual grade controls in our template without having to traverse the tree from themyform instance.

Nosotros tin therefore re-write thewordy firstName ngClass expression to something much more succinct, like so:

                  <div grade="form-group"  [ngClass]="{   'has-danger': firstName.invalid && firstName.muddy,   'has-success': firstName.valid && firstName.dirty }">                

Validation Letters

Equally well equally styling a course when information technology'due south invalid it's as well useful to show the user error messages with helpful hints about how they tin can make the form valid again.

Taking what we take learnt about form validation styling we can apply the aforementioned method to conditionally show or hide an fault bulletin.

Bootstrap conveniently has some markup and classes for form controls which nosotros tin can employ to bear witness these error messages, let's add them to our password grade command, like so:

                <div class="grade-grouping">   <characterization>Password</characterization>   <input type="password"          class="form-control"          formControlName="countersign">   <div class="course-control-feedback"                                    (1)                  *ngIf="password.invalid && password.dingy">                                    (two)                  Field is invalid   </div> </div>              
  1. The form grade-command-feedback shows a message in cherry if the parent form-grouping div also has thehas-danger class, i.e. when the field is invalid any text under this div will show as ruby.

  2. We only bear witness the message when the password field is both invalid anddirty.

Now when the input control is both dirty andinvalid we show the validation error message "Field is invalid".

However this field has two validators associated with it, the required validator and the minlength validator simply with the to a higher place solution we only testify ane generic validation fault bulletin. We can't tell the user what they demand to exercise in order to make the field valid.

How to do nosotros show a separate validation mistake message for each of the validators?

Nosotros can practise that by checking another property on our class control chosen errors.

This is an object which has one entry per validator, the key is the proper name of the validator and if the value is not null then the validator is failing.

                <div grade="form-command-feedback" *ngIf="countersign.errors && (password.dirty || password.touched)">   <p *ngIf="countersign.errors.required">Countersign is required</p>   <p *ngIf="password.errors.minlength">Password must be 8 characters long</p> </div>              

Note

If theerrors object has a key ofrequired it means the command is failing because it's required and the user hasn't entered any value into the input field.

Digging a flake deeper into theerrors belongings. The value tin can contain useful bits of information which nosotros can show the user, for instance theminlength validator gives u.s. therequiredLength andactualLength properties.

                {   "minlength": {     "requiredLength": 8,     "actualLength": one   } }              

We can employ this in our validation mistake message to requite the user a bit more help in resolving the issue, like and so:

                <div class="form-command-feedback"      *ngIf="countersign.errors && (password.dirty || password.touched)">   <p *ngIf="password.errors.required">Password is required</p>   <p *ngIf="password.errors.minlength">Password must be 8 characters long, we need another {{password.errors.minlength.requiredLength - password.errors.minlength.actualLength}} characters </p> </div>              

password validation messages

Figure 3. Form Validation Letters

Summary

Nosotros can add validators to our model form which check each field for validity.

Can render the controls with styling to show the user when fields are invalid.

Finally, we can add validation error messages then the user knows how to make the form valid again.

Next up we'll look at how to submit and reset a model-driven grade.

List

Listing ii. main.ts

                import {     NgModule,     Component,     Pipe,     OnInit } from '@angular/core'; import {     ReactiveFormsModule,     FormsModule,     FormGroup,     FormControl,     Validators,     FormBuilder } from '@angular/forms'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';   @Component({   selector: 'model-class',   template: `   <div class="container">   <form novalidate       [formGroup]="myform">    <fieldset formGroupName="proper name">     <div class="grade-grouping"          [ngClass]="{         'has-danger': firstName.invalid && (firstName.dirty || firstName.touched),         'has-success': firstName.valid && (firstName.dirty || firstName.touched)       }">       <label>Start Name</label>       <input blazon="text"              class="form-command"              formControlName="firstName"              required>       <div class="class-control-feedback"            *ngIf="firstName.errors && (firstName.muddied || firstName.touched)">         <p *ngIf="firstName.errors.required">Commencement Proper name is required</p>       </div>        <!--         <pre>Valid? {{ myform.controls.name.controls.firstName.valid }}</pre>         <pre>Muddy? {{ myform.controls.name.controls.firstName.dirty }}</pre>       -->     </div>      <div course="grade-grouping"          [ngClass]="{         'has-danger': lastName.invalid && (lastName.dirty || lastName.touched),         'has-success': lastName.valid && (lastName.dingy || lastName.touched)       }">       <label>Terminal Proper name</label>       <input blazon="text"              grade="form-control"              formControlName="lastName"              required>       <div class="form-control-feedback"            *ngIf="lastName.errors && (lastName.muddied || lastName.touched)">         <p *ngIf="lastName.errors.required">Final Name is required</p>       </div>     </div>   </fieldset>     <div grade="form-group"        [ngClass]="{         'has-danger': email.invalid && (email.dirty || email.touched),         'has-success': email.valid && (email.dingy || email.touched)    }">     <characterization>Email</label>     <input type="email"            course="form-control"            formControlName="email"            required>     <div class="course-control-feedback"          *ngIf="email.errors && (e-mail.muddy || email.touched)">       <p *ngIf="email.errors.required">Email is required</p>       <p *ngIf="password.errors.pattern">The email address must incorporate at least the @ character</p>     </div>      <!--       <pre>Valid? {{ myform.controls.electronic mail.valid }}</pre>       <pre>Dingy? {{ myform.controls.electronic mail.dirty }}</pre>     -->    </div>    <div class="form-group"        [ngClass]="{         'has-danger': countersign.invalid && (countersign.dirty || password.touched),         'has-success': password.valid && (countersign.muddy || password.touched)    }">     <label>Countersign</label>     <input type="password"            class="form-command"            formControlName="password"            required>     <div class="form-command-feedback"          *ngIf="password.errors && (password.dirty || password.touched)">       <p *ngIf="countersign.errors.required">Password is required</p>       <p *ngIf="password.errors.minlength">Password must be 8 characters long, we need another {{password.errors.minlength.requiredLength - password.errors.minlength.actualLength}} characters </p>     </div>   </div>    <!--     <pre>{{ countersign.errors | json }}</pre>   -->    <div class="form-group"        [ngClass]="{         'has-danger': language.invalid && (language.muddy || language.touched),         'has-success': language.valid && (language.dirty || language.touched)       }">     <label>Linguistic communication</label>     <select class="form-command"             formControlName="linguistic communication">       <choice value="">Please select a language</choice>       <option *ngFor="permit lang of langs"               [value]="lang">{{lang}}       </pick>     </select>   </div>    <pre>{{myform.value | json}}</pre> </course> </div>` }) class ModelFormComponent implements OnInit {   langs: string[] = [     'English language',     'French',     'German language',   ];   myform: FormGroup;   firstName: FormControl;   lastName: FormControl;   email: FormControl;   password: FormControl;   linguistic communication: FormControl;     ngOnInit() {     this.createFormControls();     this.createForm();   }    createFormControls() {     this.firstName = new FormControl('', Validators.required);     this.lastName = new FormControl('', Validators.required);     this.electronic mail = new FormControl('', [       Validators.required,       Validators.blueprint("[^ @]*@[^ @]*")     ]);     this.password = new FormControl('', [       Validators.required,       Validators.minLength(8)     ]);     this.language = new FormControl('');   }    createForm() {     this.myform = new FormGroup({       name: new FormGroup({         firstName: this.firstName,         lastName: this.lastName,       }),       email: this.email,       password: this.countersign,       language: this.language     });   } }   @Component({   selector: 'app',   template: `<model-form></model-form>` }) class AppComponent { }   @NgModule({   imports: [     BrowserModule,     FormsModule,     ReactiveFormsModule],   declarations: [     AppComponent,     ModelFormComponent   ],   bootstrap: [     AppComponent   ], }) class AppModule { }  platformBrowserDynamic().bootstrapModule(AppModule);              

peachdoetince.blogspot.com

Source: https://codecraft.tv/courses/angular/forms/model-driven-validation/