You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If a validation error exists, you can access it in a FormControl's errors property
<input#firstname="ngModel" requiredminlength="3"><!-- IF minlength validation error state is "true" --><p*ngIf="firstname.errors.minlength">
oops!
</p>
Hint: this can also be applied as a class, using [ngClass]..!
ngForm, ngModelGroup, ngModel keeps record of the following states, which are boolean:
Dirty / Pristine (did user change value of control?)
Touched / Untouched (did user focus on control and move away?)
Valid / Invalid (did user input pass all validation ?)
Access them like this:
<form#form="ngForm"><inputngModelname="firstname" required><div*ngIf="!firstname.valid && !firstname.pristine">
show this div if firstname field is either invalid or dirty
</div><button[disabled]="!form.valid">Save</button></form>
Custom Validators! π§
A validator is a function
It takes FormControl as an input
... and returns null (successful validation)
... or returns an error object (contains validation error)
Custom Validator (for template driven form) example
Steps:
ng generate directive [YOUR_VALIDATOR_NAME]
Write your validator function in the directive and reference it in providers attribute
Import and declare the directive in your @NgModule declarations array
import{Directive}from'@angular/core';import{FormControl}from'@angular/forms/src/model';import{NG_VALIDATORS}from'@angular/forms';constVALID_EMAIL=[SOME_REGEX_FOR_EMAIL_VALIDATION];// 1. export the function by itselfexportfunctionvalidateEmail(c: FormControl){varvalid=VALID_EMAIL.test(c.value);// 1a. if INVALID: return error message// 1b. if VALID: return nullreturn(!valid) ? {isEmailValid: {value: false}} : null;}// 2. Reference the exported function in useValue attribute
@Directive({// Angular will look for input tags with bothselector: '[validateEmail][ngModel]',providers: [{provide: NG_VALIDATORS,useValue: validateEmail,// this is called multi provider// it allows multiple deps for a single token// NG_VALIDATORS is a built-in multi providermulti: true}]})exportclassEmailValidatorDirective{constructor(){}}
Custom Async Validators! β¨π§β¨
Used when you need validation, but you require some service (or some external dependency) to perform it
Returns an Observable (or Promise)
Similar beginnings as writing custom sync validators, but has a couple big differences.
Steps:
ng generate directive [VALIDATOR_NAME]
Write a function that returns a function(!)
Do a depedency injection in the constructor of the directive
Implement AsyncValidator in the class and return the function above inside the validate(c: FormControl) method that's implemented.
Import and declare the directive in your @NgModule declarations array
Use it in the template by calling the selectors chosen in the directive
See example in next page...
import{ContactsService}from'./contacts.service';import{Directive,forwardRef}from'@angular/core';import{NG_ASYNC_VALIDATORS,FormControl,AsyncValidator}from'@angular/forms';import{map}from'rxjs/operators';// factory function: a function that returns a function// it requires injection of contactsServiceexportfunctioncheckEmailAvail(ctSvc: ContactsService){return(c: FormControl)=>{returnctSvc.isEmailAvailable(c.value)/* ctSvc.isEmailAvailable() response body: { msg?: string, error?: string } if response body does NOT contain error --> return null, ELSE return { emailTaken: true } */.pipe(map(response=>!response.error
? null
: {emailTaken: true}));};}
@Directive({selector: '[emailAvailValidator][ngModel]',providers: [{provide: NG_ASYNC_VALIDATORS,useExisting:
forwardRef(()=>EmailAvailValidatorDirective),multi: true}]})exportclassEmailAvailValidatorDirectiveimplementsAsyncValidator{_validate: Function;// service injection in the constructorconstructor(contactSvc: ContactsService){this._validate=checkEmailAvail(contactSvc);}validate(c: FormControl){returnthis._validate(c);}}
Q&A about example in previous slide π
Q: What's that forwardRef() thing in the provider attribute of @Directive?
A: It's kind of like hoisting. We need to do this because we want to have a class injected that we created in the same file.
Q: Is there an order of execution for different validators?
A: Yes. Angular executes sync validators before async validators.
NB: Make sure the template [formGroup] and formControlName attribute matches what you are composing in the component! They still need to correlate to each other...
Let's apply (Sync) validators to our Reactive Form! π¨
Import Validators into component
Edit each FormControl to be an array, pass it in as the second argument.
If you have multiple validators, compose it via an array
If you are using a custom validator, then import the Validator function (not the directive)
<divformArrayName="phone"><div*ngFor="let phone of form.get('phone').controls; let i = index; let l = last;"><mat-input-container><input[formControlName]="i" matInputplaceholder="Phone"></mat-input-container><buttonmat-icon-buttontype="button"
*ngIf="i >= 1"
(click)="removePhoneField(i)"><mat-icon>highlight_off</mat-icon></button><buttonmat-icon-buttontype="button"
*ngIf="l && phone.value != '' && i <= 3"
(click)="addPhoneField()"><mat-icon>add_circle_outline</mat-icon></button></div></div>