angular2异步表单验证 [英] angular2 async form validation
问题描述
我正在尝试使用Angular2完成一部分表单验证.
I am trying to accomplish a piece of a form validation with Angular2.
我试图通过异步调用来查找用户名是否已经在我的数据库中使用并使用过.
I am trying to find out, via an asynchronous call, if a username has already been taken and used in my database.
到目前为止,这是我的代码:
Here is my code so far:
表格组件:
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返回未定义.
NEW CODE (UPDATED x2). ControlGroup is returning 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>
推荐答案
您应通过以下方式定义异步验证器:
You should define your async validator this way:
'userName': ['', ValidationService.userNameValidator,
ValidationService.userNameIsTaken],
,而不是Validators.compose
方法.实际上,这是对应于哪些参数:
And not with the Validators.compose
method. As a matter of fact, here is what parameters correspond to:
'<field-name>': [ '', syncValidators, asyncValidators ]
此外,当不使用用户名而不是`{taken:false}
Moreover you should resolve with null when the user name isn't taken instead of `{taken: false}
if (data.userName == true) {
resolve({ taken: true })
} else {
resolve(null);
}
有关更多详细信息,请参见本文(字段的异步验证"部分):
See this article for more details (section "Asynchronous validation for fields"):
修改
也许我的答案不够清楚.您仍然需要使用Validators.compose
,但仅当您具有多个同步验证器时才可以使用:
Perhaps my answer isn't clear enough. You still need to use Validators.compose
but only when you have several synchronous validators:
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]
});
};
编辑1
您需要利用ngFormControl
而不是ngControl
,因为您使用FormBuilder
类定义了控件.
You need to leverage the ngFormControl
instead of the ngControl
one because you define your controls using the FormBuilder
class.
<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>
有关更多详细信息,请参见本文:
See this article for more details:
这篇关于angular2异步表单验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!