Angular 2 Multiple validators

匿名 (未验证) 提交于 2019-12-03 02:49:01

问题:

Is it possible to have multiple validators on a form field? I tried this, but it resulted in some strange errors (field was never valid, even if requirements were met)

this.username = new Control('', Validators.minLength(5), Validators.required); 

How can I use multiple validators?

回答1:

You can combine validators using Validators.compose()

this.username = new Control('',      Validators.compose(         [Validators.minLength(5), Validators.required])); 

for async validators use

this.username = new Control('', null,     Validators.composeAsync(         [someAsyncValidator, otherAsyncValidator])); 

There are open issues with async validators, especially sync validators combined with async validators don't work

To make sync validators work with async validators, wrap the sync validators in promises and compose them as async valdiators like

this.username = new Control('', null,     Validators.composeAsync([         (control:Control) => Promise.resolve(Validators.minLength(5)(control)),          (control:Control) => Promise.resolve(Validators.required(control)),         someAsyncValidator, otherAsyncValidator     ])); 


回答2:

this problem has been addressed

you can make an array of validators

this.username = new FormControl('', [ Validators.minLength(5), Validators.required ]);  


回答3:

I suggest utilizing the Validators.compose() method for combining all non-async validators and separately passing in the Validators.composeAsync() for any async calls.

Basically the constructor args for FormControl are as follows:

  1. formState (or simply the initial starting value)
  2. validators (I suggest using Validators.compose([...]) here)
  3. asyncValidators (I suggest using Validators.composeAsync([...]) here)

Example using FormBuilder (feel free to use straight up Control):

this.acctForm = this.fb.group({             'name': [                 '',                 Validators.compose([                     Validators.required, Validators.minLength(2), Validators.maxLength(20), Validators.pattern('[a-zA-Z]')                 ])             ],             'cellNumber': [                 '',                 Validators.compose([                     Validators.required, Validators.pattern('[0-9]{10}')                 ]),                 Validators.composeAsync([                     this.checkPhoneValid.bind(this)                 ])             ]         }); 

This helps avoid async validation until the non-async validators are valid (excl. the initial check, which can easily be handled, see further below).

Everything Combined Example (validators, asyncValidators & debouncing):

import { Component, Injectable, OnInit } from '@angular/core'; import { Http } from '@angular/http'; import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';   @Component({     selector: 'app-sandbox',     templateUrl: './sandbox.component.html',     providers: [] }) export class FormControlsDemoComponent implements OnInit {     private debouncedTimeout;     public acctForm: FormGroup;      constructor(private http: Http, private fb: FormBuilder) {         // @note Http should never be directly injected into a component, for simplified demo sake...     }      ngOnInit() {         this.acctForm = this.fb.group({             // Simple Example with Multiple Validators (non-async)             'name': [                 '',                 Validators.compose([                     Validators.required, Validators.minLength(2), Validators.maxLength(20), Validators.pattern('[a-zA-Z]')                 ])             ],             // Example which utilizes both Standard Validators with an Async Validator             'cellNumber': [                 '',                 Validators.compose([                     Validators.required, Validators.minLength(4), Validators.maxLength(15), Validators.pattern('[0-9]{10}')                 ]),                 Validators.composeAsync([                     this.checkPhoneValid.bind(this) // Important to bind 'this' (otherwise local member context is lost)                     /*                         @note if using a service method, it would look something like this...                         @example:                             this.myValidatorService.phoneUniq.bind(this.myValidatorService)                     */                 ])             ],             // Example with both, but Async is implicitly Debounced             'userName': [                 '',                 Validators.compose([                     Validators.required, Validators.minLength(4), Validators.maxLength(15), Validators.pattern('[a-zA-Z0-9_-]')                 ]),                 Validators.composeAsync([                     this.checkUserUniq.bind(this) // @see above async validator notes regarding use of bind                 ])             ]         });      }      /**      * Demo AsyncValidator Method      * @note - This should be in a service      */     private checkPhoneValid(control: AbstractControl): Promise<any> {         // Avoids initial check against an empty string         if (!control.value.length) {             Promise.resolve(null);         }          const q = new Promise((resolve, reject) => {             // determine result from an http response or something...             let result = true;              if (result) {                 resolve(null);             } else {                 resolve({'phoneValidCheck': false});             }         });         return q;     }      /**      * Demo AsyncValidator Method (Debounced)      * @note - This should be in a service      */     private checkUserUniq(control: AbstractControl): Promise<any> {         // Avoids initial check against an empty string         if (!control.value.length) {             Promise.resolve(null);         }          clearTimeout(this.debouncedTimeout);          const q = new Promise((resolve, reject) => {              this.debouncedTimeout = setTimeout(() => {                  const req = this.http                     .post('/some/endpoint', { check: control.value })                     .map(res => {                         // some handler logic...                         return res;                     });                  req.subscribe(isUniq => {                     if (isUniq) {                         resolve(null);                     } else {                         resolve({'usernameUnique': false });                     }                 });              }, 300);         });         return q;     }  } 

Some people like to hack in debounced async validator by binding to the control's Observable valueChanges like this:

this.someControl.debounceTime(300).subscribe(val => {       // async call... }); 

I personally don't recommend this for most cases, as it adds unnecessary complications.

NOTE: This should work, as of the latest version of Angular (2 & 4) since the writing of this post.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!