Typescript 2个多验证器

Typescript 2个多验证器,typescript,angular,Typescript,Angular,一个表单字段上是否可以有多个验证器?我尝试了这个,但它导致了一些奇怪的错误(字段永远无效,即使满足了要求) 如何使用多个验证器?您可以使用validators.compose()组合验证器。 对于异步验证程序,请使用 this.username = new Control('', null, Validators.composeAsync( [someAsyncValidator, otherAsyncValidator])); 异步验证器存在开放性问题,特别是与异步验

一个表单字段上是否可以有多个验证器?我尝试了这个,但它导致了一些奇怪的错误(字段永远无效,即使满足了要求)


如何使用多个验证器?

您可以使用
validators.compose()组合验证器。

对于异步验证程序,请使用

this.username = new Control('', null,
    Validators.composeAsync(
        [someAsyncValidator, otherAsyncValidator]));
异步验证器存在开放性问题,特别是与异步验证器结合使用的同步验证器无法工作

要使同步验证器与异步验证器一起工作,请将同步验证器包装在承诺中,并将其组合为异步Valdiator,如

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

我建议使用Validators.compose()方法组合所有非异步验证器,并分别为任何异步调用传入Validators.composeSync()

FormControl的构造函数参数基本上如下所示:

  • formState(或只是初始起始值)
  • 验证器(我建议使用验证器。在这里编写([…]))
  • asyncValidators(我建议在这里使用Validators.composeSync([…]))
  • 使用FormBuilder的示例(可以随意使用直接向上控件):

    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)
                    ])
                ]
            });
    
    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;
        }
    
    }
    
    这有助于在非异步验证器有效之前避免异步验证(不包括初始检查,可以轻松处理,请参阅下文)

    所有组合的示例(验证器、异步验证器和取消公告):

    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)
                    ])
                ]
            });
    
    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;
        }
    
    }
    
    我个人不建议大多数情况下这样做,因为这会增加不必要的并发症

    注意:自 写这篇文章


    这个问题已经得到解决

    您可以创建一个验证程序数组

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

    谢谢你的解决方案!有一个问题:Validators.compose([//myValidators])和不使用Validators.compose()而将Validators作为第三个参数作为数组传递之间有什么区别?其实没有什么区别。就我所记得的,在我发布这个答案时还不支持仅仅传递它们(至少异步验证程序不支持)。关于同步和异步验证程序组合的问题似乎已经解决了!嗨,你能看看这个吗?谢谢。如果输入为空,我想应该返回
    Promise.resolve(null)
    。使用
    compose
    有什么区别?
    this.username = new FormControl('', [ Validators.minLength(5), Validators.required ]);