--
--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Tag:

06
2017

[旧版]Angular2でパスワード再入力みたいなバリデーションを作る

CATEGORYJavaScript
Angular2/4の標準のバリデータには、複数のフォームを比較するみたいなものは用意されてないっぽいので、それの作り方。
参考にしたのはこちらのページ。でもところどころ改良したので、以下説明。
よりイケてる実装発見したのでそちらをご覧ください。この記事は参考用&自省用に残します。

まず最初にサンプルソースの抜粋から。こんな感じになる。

equals-validator.directive.ts
import { Directive, forwardRef, Attribute } from '@angular/core';
import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';

@Directive({
selector: '[validateEquals][formControlName],[validateEquals][formControl],[validateEquals][ngModel]',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualsValidator), multi: true }
]
})
export class EqualsValidator implements Validator {
constructor(
@Attribute('validateEquals') public validateEquals: string) {
}

validate(c: AbstractControl): { [key: string]: any } {
let v = c.value;
let e = c.root.get(this.validateEquals);

if (e && v != e.value) {
return {
validateEquals: true
}
}
return null;
}
}

app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { SampleComponent } from './sample.component';
import { EqualsValidator } from './equals-validator.directive';

@NgModule({
imports: [
BrowserModule,
FormsModule,
],
declarations: [
SampleComponent,
EqualsValidator,
],
bootstrap: [SampleComponent]
})
export class AppModule { }

sample.component.ts
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
selector: 'sample-form',
templateUrl: 'app/sample.html',
})
export class SampleComponent {
sampleForm = { password: '', passwordConfirm: '' };

submit(): void {
console.log(this.sampleForm);
}

forceValidation(f: NgForm): void {
setTimeout(() => {
for (let key in f.form.controls) {
f.form.controls[key].updateValueAndValidity();
}
}, 100);
}
}

sample.html
<form #f="ngForm" (ngSubmit)="submit()" novalidate>
<input type="password" name="password" [(ngModel)]="sampleForm.password" #password="ngModel" (ngModelChange)="forceValidation(f)" required>
<div *ngIf="password.errors">
<div [hidden]="!password.errors.required">パスワードを入力してください</div>
</div>
<input type="password" name="passwordConfirm" [(ngModel)]="sampleForm.passwordConfirm" #passwordConfirm="ngModel" (ngModelChange)="forceValidation(f)" required validateEquals="password">
<div *ngIf="passwordConfirm.errors">
<div [hidden]="!passwordConfirm.errors.required">確認用パスワードを入力してください</div>
<div [hidden]="!passwordConfirm.errors.validateEquals">確認用パスワードが一致しません</div>
</div>
<button type="submit" [disabled]="!f.form.valid">送信</button>
</form>

で、解説。Directive定義とかはサンプルそのままなので説明割愛(というかあんま分かってない(--;)。
ポイントになるのは、バリデーターの @Attribute('validateEquals') により要素の属性で比較先の変数名を指定して、c.root.get() でその要素を取ってきているところ。
ここで一点注意が必要なのは、c.root.get() では(HTML上で)自分より後に宣言されている要素しか取れないっぽいということ。
取れても良さそうなのに謎。少なくともAngular4現在取れずハマった。なので注意。
だからこのバリデータは、「パスワード再入力」欄の方に設定する必要がある。

で、それだけだと後から「パスワード」を変更したときに「パスワード再入力」欄のバリデーションが実行されずOKのままになる、という別の問題が発生する。
そこで行っているのが、(ngModelChange)="forceValidation(f)" で、これで値変更時にフォームの全要素のバリデーションを再実行させることで、問題を解決している。

かつ、ngModelChange のタイミングだとまだ入力値がモデルに反映されていないので、setTimeout()
で適当にウェイトを入れることでその問題にも対処している!
…うん正直 setTimeout() についてはバッドノウハウ感が漂ってるので、よりよい解決方法があったらコメントくださいあったorz
(たぶん別のイベントを使えばよさげ。)


なお、実は完全一致だけなら ng2-validation というライブラリに equalTo というのがあるのでそれを使えばOK。
でも日時の比較とかいろいろ他にも使うことはあるので、そういうときはこうやって自分で実装すると良し。


PS. とここまで書いた後に equalTo のソース見たら、subscribe とか駆使して ngModelChange 不要にもっと綺麗にやってた…ま、まあこれも一つの試行錯誤ということで(--;
スポンサーサイト

Tag: JavaScript TypeScript Angular

0 Comments

Leave a comment

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。