2016-04-06 140 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