首页 文章

angular2异步表单验证

提问于
浏览
6

我正在尝试使用Angular2完成一个表单验证 .

我试图通过异步调用找出已经在我的数据库中使用和使用的用户名 .

到目前为止,这是我的代码:

表格组成部分:

import {Component, OnInit} from 'angular2/core';
import {FORM_PROVIDERS, Control, ControlGroup, FormBuilder, Validators} from 'angular2/common';
import {Http, Headers, RequestOptions} from 'angular2/http';
import {ROUTER_DIRECTIVES, Router, RouteParams} from 'angular2/router';
import {ControlMessages} from './control.messages';
import {ValidationService} from './validation.service';

@Component({
    selector: 'account-form',
    templateUrl: './app/account/account.form.component.html',
    providers: [ROUTER_DIRECTIVES, CaseDataService],
    directives: [ControlMessages]
})

accountForm: ControlGroup;

constructor(private _accountService: AccountDataService,
    private _formBuilder: FormBuilder, private _router: Router, private _params?: RouteParams) {
    this.model = this._accountService.getUser();

    this.accountForm = this._formBuilder.group({
        'firstName': ['', Validators.required],
        'lastName': ['', Validators.required],
        'userName': ['', Validators.compose([ValidationService.userNameValidator, ValidationService.userNameIsTaken])],

....
}

验证服务:

export class ValidationService {


static getValidatorErrorMessage(code: string) {
    let config = {
        'required': 'Required',
        'invalidEmailAddress': 'Invalid email address',
        'invalidPassword': 'Invalid password. Password must be at least 6 characters long, and contain a number.',
        'mismatchedPasswords': 'Passwords do not match.',
        'startsWithNumber': 'Username cannot start with a number.'
    };
    return config[code];
}

static userNameValidator(control, service, Headers) {
    // Username cannot start with a number
    if (!control.value.match(/^(?:[0-9])/)) {
        return null;
    } else {
        return { 'startsWithNumber': true };
    }
}
  // NEEDS TO BE AN ASYNC CALL TO DATABASE to check if userName exists. 
// COULD userNameIsTaken be combined with userNameValidator??

static userNameIsTaken(control: Control) {
    return new Promise(resolve => {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json')

        // needs to call api route - _http will be my data service. How to include that?

        this._http.get('ROUTE GOES HERE', { headers: headers })
            .map(res => res.json())
            .subscribe(data => {
                console.log(data);
                if (data.userName == true) {
                    resolve({ taken: true })
                }
                else { resolve({ taken: false }); }
            })
    });
}
}

新代码(更新x2) . ControlGroup返回undefined .

this.form = this.accountForm;
    this.accountForm = this._formBuilder.group({
        'firstName': ['', Validators.required],
        'lastName': ['', Validators.required],
        'userName': ['', Validators.compose([Validators.required, this.accountValidationService.userNameValidator]), this.userNameIsTaken(this.form, 'userName')],
        'email': ['', Validators.compose([Validators.required, this.accountValidationService.emailValidator])],
        'password': ['', Validators.compose([Validators.required, this.accountValidationService.passwordValidator])],
        'confirm': ['', Validators.required]
    });         
};

userNameIsTaken(group: any, userName: string) {
    return new Promise(resolve => {

        this._accountService.read('/username/' + group.controls[userName].value)
            .subscribe(data => {
                data = data
                if (data) {
                    resolve({ taken: true })
                } else {
                    resolve(null);
                }
            });
    })
};

HTML:

<div class="input-group">
    <span class="input-group-label">Username</span>
    <input class="input-group-field" type="text" required [(ngModel)]="model.userName" ngControl="userName" #userName="ngForm">
    <control-messages control="userName"></control-messages>
    <div *ngIf="taken">Username is already in use.</div>
</div>

1 回答

  • 5

    您应该以这种方式定义异步验证器:

    'userName': ['', ValidationService.userNameValidator, 
           ValidationService.userNameIsTaken],
    

    而不是使用 Validators.compose 方法 . 事实上,这里的参数对应于:

    '<field-name>': [ '', syncValidators, asyncValidators ]
    

    此外,如果不采用用户名而不是`{taken:false},则应该使用null解析

    if (data.userName == true) {
      resolve({ taken: true })
    } else {
      resolve(null);
    }
    

    有关更多详细信息,请参阅此文章(“字段的异步验证”部分):

    Edit

    也许我的答案不够明确 . 您仍然需要使用 Validators.compose ,但仅当您有多个同步验证器时:

    this.accountForm = this._formBuilder.group({
        'firstName': ['', Validators.required],
        'lastName': ['', Validators.required],
        'userName': ['', Validators.compose([
                 Validators.required,
                 this.accountValidationService.userNameValidator
              ], this.userNameIsTaken],
        'email': ['', Validators.compose([
                 Validators.required,
                 this.accountValidationService.emailValidator
              ]],
        'password': ['', Validators.compose([
                 Validators.required,
                 this.accountValidationService.passwordValidator
              ]],
        'confirm': ['', Validators.required]
      });         
    };
    

    Edit1

    您需要利用 ngFormControl 而不是 ngControl ,因为您使用 FormBuilder 类定义控件 .

    <div class="input-group">
      <span class="input-group-label">Username</span>
      <input class="input-group-field" type="text" required [(ngModel)]="model.userName" [ngControl]="accountForm.controls.userName" >
      <control-messages [control]="accountForm.controls.userName"></control-messages>
      <div *ngIf="accountForm.controls.userName.errors && accountForm.controls.userName.errors.taken">Username is already in use.</div>
    </div>
    

    有关详细信息,请参阅此文章:

相关问题