Reactive Forms Make Errors Not Show Up Again
- Learning Objectives
- Validators
- Class Command State
- Muddied & Pristine
- Touched & Untouched
- Valid & Invalid
- Validation Styling
- Writing Shorter Validation Expressions
- Validation Messages
- Summary
- List
Learning Objectives
-
How to add validation checks to our form via the form model.
-
How tostyle our form in lodge to requite visual feedback to the user and then they know when the fields don't laissez passer the validation checks.
-
How to add validation error messages to the form to requite hints to the user about why the field isn't passing a validation cheque.
Validators
Carrying on from the model-driven form we started in the previous lecture.
Our form is valid all the fourth dimension, 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 command is said to exist invalid.
Since it's a signup form most of the fields should be required and I would want to specify some more complex validators on the countersign field to brand sure the user is entering a skillful potent password.
We tin can apply validators either by adding 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 directly.
Angular comes with a small prepare of pre-built validators to match the ones we can ascertain via standard HTMLv attributes, namely required
, minlegth
, maxlength
anddesign
which we can access from theValidators
module.
The first parameter of a FormControl
constructor is the initial value of the command, we'll leave that as empty string. The 2nd parameter contains either a unmarried validator if we only want to apply one, or a list of validators if nosotros want to employ multiple validators to a single control.
Our model so looks something similar this:
import { FormGroup, FormControl, Validators } from '@angular/forms'; . . . grade ModelFormComponent implements OnInit { myform: FormGroup; ngOnInit() { myform = new FormGroup({ name: new FormGroup({ firstName: new FormControl('', Validators.required), (i) lastName: new FormControl('', Validators.required), }), email: new FormControl('', [ (ii) Validators.required, Validators.pattern("[^ @]*@[^ @]*") (iii) ]), password: new FormControl('', [ Validators.minLength(8), (4) Validators.required ]), linguistic communication: new FormControl() (5) }); } }
1 | Nosotros add a single required validator to mark this control as required. |
2 | We tin can also provide an array of validators. |
3 | We specify a blueprint validator which checks whether the email contains a@ character. |
iv | Theminlength validator checks to see if the countersign is a minimum of eight characters long. |
five | We don't add whatever validators to the language select box. |
Form Command Land
The form command instance on our model encapsulates state most the control itself, such as if it is currently valid or if it'due south been touched.
Dirty & Pristine
We can go a reference to these form command instances in our template through thecontrols
property of our myform
model, for case we can impress out the the dingy state of the email field like then:
<pre>Dingy? {{ myform.controls.email.dirty }}</pre>
dirty
is true
if the user has changed the value of the control.
The opposite ofdingy
is pristine
and then if we wrote:
<pre>Pristine? {{ myform.controls.e-mail.pristine }}</pre>
This would be true
if the user hasn't changed the value, andfalse
if the user has.
Touched & Untouched
A controls is said to be touched if the the user focused on the control and then focused on something else. For example by clicking into the control and then pressing tab or clicking on another control in the grade.
The difference between touched
anddirty
is that with touched the user doesn't need to really modify the value of the input control.
<pre>Touched? {{ myform.controls.email.touched }}</pre>
touched
is true
of the field has been touched by the user, otherwise information technology's false.
The opposite oftouched
is the property untouched
.
Valid & Invalid
We can also cheque thevalid
country of the control with:
<pre>Valid? {{ myform.controls.email.valid }}</pre>
valid
is true
of the field doesn't take any validators or if all the validators are passing.
Again 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 andsimulated
if it was valid.
Validation Styling
Bootstrap has classes for showing visual feedback for grade controls when they are invalid.
For instance if nosotros add thehas-danger
class to the parent div
of the input control with the class ofclass-group
it adds ared border.
Conversely if we add thehas-success
class it adds agreenish border.
Figure 1. Valid Form Control
Effigy 2. Invalid Grade Control
Nosotros tin can combine Bootstrap classes with dirty
andinvalid
FormControl
properties and the ngClass
directive to give the user some nice visual feedback, like so:
<div grade="form-group" [ngClass]="{ 'has-danger': myform.controls.electronic mail.invalid && myform.controls.e-mail.dirty, (i) 'has-success': myform.controls.email.valid && myform.controls.email.dirty (ii) }">
one | If the e-mail is invalid and information technology'south been touched by the user then we add thehas-danger grade giving the control a red border. |
2 | If the email is valid and it's been touched past the user and so nosotros add together thehas-success class giving the control a light-green border. |
Tip
The reason we bank check for the dingy
holding being true is so we don't show the user visual feedback when the form is first displayed. Instead we only prove the user feedback when they have had a chance to edit the field.
Now the input command shows thedark-green border when it'southward valid
anddirty
andcarmine if it's invalid
anddirty
.
Writing Shorter Validation Expressions
The above can chop-chop get cumbersome to use in our templates, especially for things like the nested firstName
andlastName
controls.
Since thefirstName
andlastName
FormControls
exist under thename
FormGroup
to admission those from the template we need to utilise syntax like this:
<div class="form-group" [ngClass]="{ 'has-danger': myform.controls.name.controls.firstName.invalid && myform.controls.name.controls.firstName.dirty, 'has-success': myform.controls.proper noun.controls.firstName.valid && myform.controls.proper name.controls.firstName.dirty }">
The length of the expression apace becomes unwieldy.
We can help ourselves here by creating local properties on our component to reflect the individual FormControls
and binding directly to them in our template, like so:
List 1. script.ts
class ModelFormComponent implements OnInit { langs: string[] = [ 'English language', 'French', 'German', ]; myform: FormGroup; firstName: FormControl; (1) lastName: FormControl; email: FormControl; countersign: FormControl; linguistic communication: FormControl; ngOnInit() { this.createFormControls(); this.createForm(); } createFormControls() { (2) this.firstName = new FormControl('', Validators.required); this.lastName = new FormControl('', Validators.required); this.e-mail = new FormControl('', [ Validators.required, Validators.blueprint("[^ @]*@[^ @]*") ]); this.countersign = new FormControl('', [ Validators.required, Validators.minLength(8) ]); this.language = new FormControl('', Validators.required); } createForm() { (3) this.myform = new FormGroup({ name: new FormGroup({ firstName: this.firstName, lastName: this.lastName, }), email: this.email, password: this.countersign, language: this.language }); } }
1 | We declare theFormControls as properties of our component. Then we can bind to them straight in our tempalte without having to get through the peak-level myform model. |
2 | We first create theFormControls . |
3 | We then construct themyform model from the course controls we created previously and stored every bit properties on our component. |
Now we can bind straight to our individual class controls in our template without having to traverse the tree from themyform instance.
We can therefore ngClass
expression to something much more than succinct, like so:
<div class="grade-group" [ngClass]="{ 'has-danger': firstName.invalid && firstName.dirty, 'has-success': firstName.valid && firstName.muddy }">
Validation Messages
Equally well as styling a form when information technology'south invalid it's likewise useful to show the user mistake messages with helpful hints about how they can make the course valid again.
Taking what we accept learnt about form validation styling we tin can utilize the same method to conditionally show or hide an mistake bulletin.
Bootstrap conveniently has some markup and classes for form controls which we can use to show these error letters, allow's add them to our countersign form command, similar then:
<div grade="grade-group"> <label>Password</label> <input blazon="password" class="class-control" formControlName="countersign"> <div class="form-control-feedback" (i) *ngIf="countersign.invalid && password.dirty"> (2) Field is invalid </div> </div>
-
The class
class-control-feedback
shows a message in reddish if the parentform-group
div also has thehas-danger
course, i.due east. when the field is invalid whatever text under this div will show as red. -
We only evidence the message when the countersign field is both
invalid
andmuddy
.
At present when the input control is both dirty
andinvalid
we show the validation error message "Field is invalid"
.
Notwithstanding this field has two validators associated with information technology, the required validator and the minlength validator but with the above solution nosotros just show one generic validation error message. We can't tell the user what they need to exercise in order to make the field valid.
How to do we show a split validation error message for each of the validators?
Nosotros can do that past checking another property on our form control called errors
.
This is an object which has one entry per validator, the cardinal is the proper noun of the validator and if the value is not zippo so the validator is failing.
<div class="form-control-feedback" *ngIf="password.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'southward required
and the user hasn't entered any value into the input field.
Digging a chip deeper into theerrors
property. The value tin can contain useful bits of information which we tin can testify the user, for example theminlength
validator gives us therequiredLength
andactualLength
properties.
{ "minlength": { "requiredLength": 8, "actualLength": 1 } }
We can use this in our validation mistake message to give the user a bit more than assistance in resolving the upshot, like so:
<div class="form-control-feedback" *ngIf="password.errors && (password.muddy || password.touched)"> <p *ngIf="countersign.errors.required">Countersign is required</p> <p *ngIf="countersign.errors.minlength">Countersign must be 8 characters long, we need another {{password.errors.minlength.requiredLength - password.errors.minlength.actualLength}} characters </p> </div>
Effigy 3. Course Validation Letters
Summary
We tin add together validators to our model form which check each field for validity.
Tin can render the controls with styling to show the user when fields are invalid.
Finally, we tin add validation error messages so the user knows how to make the form valid again.
Side by side up we'll look at how to submit and reset a model-driven form.
Listing
Listing ii. primary.ts
import { NgModule, Component, Pipe, OnInit } from '@angular/core'; import { ReactiveFormsModule, FormsModule, FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; import {BrowserModule} from '@athwart/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; @Component({ selector: 'model-form', template: ` <div form="container"> <form novalidate [formGroup]="myform"> <fieldset formGroupName="name"> <div form="form-group" [ngClass]="{ 'has-danger': firstName.invalid && (firstName.muddied || firstName.touched), 'has-success': firstName.valid && (firstName.muddied || firstName.touched) }"> <label>First Name</label> <input type="text" form="form-control" formControlName="firstName" required> <div class="class-control-feedback" *ngIf="firstName.errors && (firstName.dirty || firstName.touched)"> <p *ngIf="firstName.errors.required">First Name is required</p> </div> <!-- <pre>Valid? {{ myform.controls.name.controls.firstName.valid }}</pre> <pre>Dirty? {{ myform.controls.proper name.controls.firstName.dirty }}</pre> --> </div> <div class="course-group" [ngClass]="{ 'has-danger': lastName.invalid && (lastName.muddy || lastName.touched), 'has-success': lastName.valid && (lastName.muddy || lastName.touched) }"> <label>Last Proper noun</characterization> <input type="text" form="class-control" formControlName="lastName" required> <div course="grade-control-feedback" *ngIf="lastName.errors && (lastName.dirty || lastName.touched)"> <p *ngIf="lastName.errors.required">Last Name is required</p> </div> </div> </fieldset> <div course="grade-group" [ngClass]="{ 'has-danger': electronic mail.invalid && (email.dirty || email.touched), 'has-success': email.valid && (email.dirty || email.touched) }"> <label>Email</label> <input type="email" class="form-control" formControlName="email" required> <div course="form-control-feedback" *ngIf="email.errors && (email.dirty || email.touched)"> <p *ngIf="email.errors.required">E-mail is required</p> <p *ngIf="password.errors.pattern">The email accost must contain at least the @ graphic symbol</p> </div> <!-- <pre>Valid? {{ myform.controls.email.valid }}</pre> <pre>Dirty? {{ myform.controls.e-mail.dirty }}</pre> --> </div> <div grade="grade-group" [ngClass]="{ 'has-danger': password.invalid && (password.muddy || password.touched), 'has-success': password.valid && (countersign.dirty || password.touched) }"> <label>Password</characterization> <input type="password" class="form-control" formControlName="password" required> <div class="form-command-feedback" *ngIf="password.errors && (password.dirty || password.touched)"> <p *ngIf="countersign.errors.required">Countersign is required</p> <p *ngIf="password.errors.minlength">Countersign must be eight characters long, we demand another {{password.errors.minlength.requiredLength - password.errors.minlength.actualLength}} characters </p> </div> </div> <!-- <pre>{{ countersign.errors | json }}</pre> --> <div grade="form-group" [ngClass]="{ 'has-danger': language.invalid && (linguistic communication.dirty || linguistic communication.touched), 'has-success': language.valid && (language.dirty || language.touched) }"> <label>Language</label> <select class="class-control" formControlName="language"> <option value="">Please select a language</option> <pick *ngFor="let lang of langs" [value]="lang">{{lang}} </option> </select> </div> <pre>{{myform.value | json}}</pre> </form> </div>` }) course ModelFormComponent implements OnInit { langs: string[] = [ 'English', 'French', 'German', ]; 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.email = new FormControl('', [ Validators.required, Validators.blueprint("[^ @]*@[^ @]*") ]); this.password = new FormControl('', [ Validators.required, Validators.minLength(eight) ]); this.language = new FormControl(''); } createForm() { this.myform = new FormGroup({ proper name: new FormGroup({ firstName: this.firstName, lastName: this.lastName, }), e-mail: this.email, password: this.password, language: this.language }); } } @Component({ selector: 'app', template: `<model-form></model-form>` }) class AppComponent { } @NgModule({ imports: [ BrowserModule, FormsModule, ReactiveFormsModule], declarations: [ AppComponent, ModelFormComponent ], bootstrap: [ AppComponent ], }) form AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
Source: https://codecraft.tv/courses/angular/forms/model-driven-validation/
0 Response to "Reactive Forms Make Errors Not Show Up Again"
Post a Comment