2016-04-06 106 views
5

我正在尝试使用Angular2完成表单验证。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> 

回答

5

你应该定义你的异步验证这种方式:

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

而不是与Validators.compose方法。作为事实上,这里是什么参数对应于:

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

而且你应该空解决将用户名不采取,而不是`{采取:假}

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

见这篇文章的更多详细资料(节 “异步验证的领域”):

编辑

也许我的答案不够清楚。你仍然需要使用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> 

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

+0

喜蒂埃里 - 谢谢你。我已经在上面添加了我的代码,但仍似乎无法启动它。 :( – wjfieseler

+0

不客气!是的,你的代码仍然存在问题,我更新了我的答案... –

+0

正在取得进展,下面是更新后的代码,但是我似乎无法访问控制组。 – wjfieseler