Angular2 模板驱动的异步验证器

Posted

技术标签:

【中文标题】Angular2 模板驱动的异步验证器【英文标题】:Angular2 template driven async validator 【发布时间】:2016-07-14 02:59:42 【问题描述】:

我在以模板驱动形式定义异步验证器时遇到问题。

目前我有这个输入:

<input type="text" ngControl="email"  [(ngModel)]="model.applicant.contact.email" #email="ngForm" required asyncEmailValidator>

带有指向此类的验证器选择器 asyncEmailValidator

import provide from "angular2/core";
import Directive from "angular2/core";
import NG_VALIDATORS from "angular2/common";
import Validator from "angular2/common";
import Control from "angular2/common";
import AccountService from "../services/account.service";

@Directive(
selector: '[asyncEmailValidator]',
providers: [provide(NG_VALIDATORS, useExisting: EmailValidator, multi: true), AccountService]
)

export class EmailValidator implements Validator 
//https://angular.io/docs/ts/latest/api/common/Validator-interface.html


constructor(private accountService:AccountService) 


validate(c:Control):[key: string]: any 
    let EMAIL_REGEXP = /^[-a-z0-9~!$%^&*_=+\'?]+(\.[-a-z0-9~!$%^&*_=+\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]1,3\.[0-9]1,3\.[0-9]1,3\.[0-9]1,3))(:[0-9]1,5)?$/i;

    if (!EMAIL_REGEXP.test(c.value)) 
        return validateEmail: valid: false;
    

    return null;

    /*return new Promise(resolve =>
        this.accountService.getUserNames(c.value).subscribe(res => 
            if (res == true) 
                resolve(null);
            
            else 
                resolve(validateEmailTaken: valid: false);
            
        ));*/

电子邮件正则表达式部分按预期工作,如果正则表达式匹配,则表单正在成功验证。但在那之后我想检查电子邮件是否还没有被使用,所以我为我的 accountService 创建了承诺。但这根本不起作用,表单一直处于失败状态。

我已经阅读了有关模型驱动表单和使用 FormBuilder 的信息,如下所示:

constructor(builder: FormBuilder) 
this.email = new Control('',
  Validators.compose([Validators.required, CustomValidators.emailFormat]), CustomValidators.duplicated
);

Control() 的第三个参数中定义了异步验证器,但这不是我的情况,因为我使用不同的方法。

所以,我的问题是:是否可以使用模板驱动的表单创建异步验证器?

【问题讨论】:

【参考方案1】:

您可以尝试使用NG_ASYNC_VALIDATORS 键而不是NG_VALIDATORS 键注册异步验证器的提供者(仅适用于同步验证器):

@Directive(
  selector: '[asyncEmailValidator]',
  providers: [
    provide(NG_ASYNC_VALIDATORS,  // <------------
      useExisting: EmailValidator, multi: true
    ),
    AccountService
  ]
)
export class EmailValidator implements Validator 
  constructor(private accountService:AccountService) 
  

  validate(c:Control) 
    return new Promise(resolve =>
      this.accountService.getUserNames(c.value).subscribe(res => 
        if (res == true) 
            resolve(null);
        
        else 
            resolve(validateEmailTaken: valid: false);
        
    ));
  

在 angular.io 网站上查看此文档:

https://angular.io/docs/ts/latest/api/forms/index/NG_ASYNC_VALIDATORS-let.html

【讨论】:

谢谢!这正是我想要的。顺便提一句。是否可以在一个验证器类中将验证器的异步部分(承诺)与 noasync 部分(只是正则表达式)结合起来,这样只有当正则表达式正常时才会启动异步部分,还是我需要它们两个? 是的,你可以,但在这两种情况下你都需要解决这个承诺。即使处理不是异步的,后者也可以解决...... 好的,我试试。再次感谢。 感谢您在 angular.io/guide/form-validation 现有文档的基础上构建,这对您有很大帮助!【参考方案2】:

值得注意的是,从那时起语法发生了变化,现在我使用的是 angular 4,下面是重写:

import  Directive, forwardRef  from '@angular/core';
import  AbstractControl, Validator, NG_ASYNC_VALIDATORS  from '@angular/forms';
import  AccountService  from 'account.service';

@Directive(
    selector: '[asyncEmailValidator]',
    providers: [
        
            provide: NG_ASYNC_VALIDATORS,
            useExisting: forwardRef(() => EmailValidatorDirective), multi: true
        ,
    ]
)
export class EmailValidatorDirective implements Validator 
    constructor(private _accountService: AccountService) 
    

    validate(c: AbstractControl) 
        return new Promise(resolve =>
            this._accountService.isEmailExists(c.value).subscribe(res => 
                if (res == true) 
                    resolve( validateEmailTaken:  valid: false  );
                
                else 
                    resolve(null);
                
            ));
    

【讨论】:

【参考方案3】:

我能够使用用户服务正确调用 validate 自定义验证器。我遇到的一个问题是,我将自定义验证器保存在 Validators.compose() 中。取出 compose 功能后一切正常。

import  Directive  from '@angular/core';
import  AsyncValidator, AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn  from '@angular/forms';
import  Observable  from 'rxjs';
import  UserService  from '../Services/user.service';
import  map  from 'rxjs/operators';

export function UniqueUsernameValidator(userService: UserService): AsyncValidatorFn 
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => 

        const q = new Promise((resolve, reject) => 
            setTimeout(() => 
                userService.isUsernameTaken(control.value).subscribe((data: any) => 
                    // console.log('top: ' + data + ' type: ' + typeof data);
                    if (data === false) 
                        resolve(null);
                     else 
                        resolve(
                            usernameTaken: 
                                valid: true
                            
                        );
                    
                , () => 
                    resolve(
                        usernameTaken: 
                            valid: false
                        
                    );
                );
            , 1000);
        );

        return q;
    ;


@Directive(
    selector: '[appUniqueUsername]',
    providers: [ provide: NG_ASYNC_VALIDATORS, useExisting: UniqueUsernameValidatorDirective, multi: true , UserService]
)
export class UniqueUsernameValidatorDirective implements AsyncValidator 
    constructor(private userService: UserService)  

    validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> 
        return UniqueUsernameValidator(this.userService)(control);
    


【讨论】:

以上是关于Angular2 模板驱动的异步验证器的主要内容,如果未能解决你的问题,请参考以下文章

异步验证器不适用于 Angular 中的模板驱动表单

angular2 formBuilder 组异步验证

如何使用 Reactive Forms (Angular2 RC3) 进行异步验证?

Angular 2 复选框验证

Angular 2单选按钮验证

如何根据 Angular 2 中的自定义验证规则显示错误消息?