4年目のアドベントカレンダー参戦になります。株式会社ピー・アール・オーです。
これまでに引き続き、今年も各自が興味のある分野について各々に書く方針で進めさせていただきます。
はじめに
angularでフォームを作成したときに無効だったボタンが一瞬だけ有効になるバグが発生し、原因を調べたところ非同期処理によるものだと分かったので以下にまとめる
参考:https://itnext.io/valid-and-invalid-in-angular-forms-61cfa3f2a0cd
Angularフォームの無効
Angualrのフォームボタンを無効にする一般的な方法はinvalidかvalidを使うかの2通りあります。
invalidとvalidの意味は
<button type="submit" [disabled]="form.invalid">送信</button>
<button type="submit" [disabled]="!form.valid">送信</button>
この違いについて考えたことはありますか?
form.invalid と!form.valid? 論理的には同じはずですが、実際にはわずかな違いがあります。
これを実証するために、非同期処理を用いた単純なフォームを作成します。
検証
<form [formGroup]="form">
<input type="text" placeholder="Enter your email" formControlName="email" />
<button type="submit" [disabled]="!form.valid">送信</button>
<div *ngIf="form.pending">
<img src="https://www.benricho.org/loading_images/img-transparent/712-32.gif" />
</div>
</form>
ボタンの無効化条件の違いを確認するだけなので非常にシンプルなフォームにしました。
フォームの送信ボタンは!form.valid
で非活性にするようにしています。
また、非同期処理中だとわかりやすいようにLodingのgifも表示するようにしています。
.ts ファイルでは、FormBuilder を使用してフォームを作成しました。
非同期処理を行うためにsetTimeout()
を使用し、カスタムバリデーターに適用しています。
form = this.fb.group({
email: ['', [Validators.required, Validators.email], this.checkValidEmail]
})
constructor(private fb: FormBuilder) {}
checkValidEmail(control: AbstractControl) {
return new Promise(resolve => {
setTimeout(() => {
resolve(null)
}, 2000)
})
}
これで準備ができたので実際にブラウザを見てみましょう。
実証
以下が送信ボタンの無効を[disabled]="!form.valid"
にした場合になります。
メールの入力後、処理中のロードが表示されロードが終了直後にボタンが有効になっています。
次に、送信ボタンの無効を[disabled]="form.invalid"
にした場合も見ていきましょう。
最初は送信ボタンが無効でしたがメールが入力されるとカスタムバリデーターの非同期処理が行われsetTimeoutで設定した2秒間送信ボタンは有効になっています。
このことから、invalid
だと非同期処理中は false
を返していることがわかります。
以下にvalid
とinvalid
の違いを図でまとめてみました。
短時間ボタンが有効になった理由は、vaild
とinvalid
のpendingの違いにあります。
まとめ
[disabled]=”form.invalid”
にした場合、フォームに非同期処理が存在するとボタンの無効が有効になったり予期せぬ動作があるので、フォームのボタンを無効にするには比較的安全な[disabled]=”!form.valid”
を使用することをお勧めします。