件名そのままなんだけど、submitしたあとにform.reset()してもinvalidが消えないので困っていた。
そんなわけある?と思ってググるとそんなわけあるようで、こんな記事を見つけた。
まさにこれなんだけど、2017年なのでさすがにv18にもなってそんなわけないだろうと思って下にスクロールしていくと現代でも解決してないようでした。
結局どうやって解決したかは以下。
<form [formGroup]="myForm" #formDirective="ngForm" (ngSubmit)="submitForm(formDirective)">
private submitForm(formDirective: FormGroupDirective): void { formDirective.resetForm(); this.myForm.reset(); }
なんでこうなるかの理由が以下にある
以下4oくんの解説
解説
1. novalidate
属性の必要性
- 要点: フォームに
novalidate
属性を付けないと、ブラウザのネイティブなバリデーションが働き、Angular の期待する動作と食い違うことがあります。novalidate
を使用することで、Angular のバリデーションロジックを優先し、予期せぬ動作を防ぐことができます。
2. Angular と DOM の相互作用
- 課題: Angular の
FormGroup
は、DOM イベント(フォームの送信やバリデーションの失敗)に関する情報を直接管理していません。FormGroup
は純粋な Angular の構造で、DOM API とは無関係です。- そのため、フォーム送信後に
FormGroup#reset()
を呼んでも、送信状態(submitted
のフラグ)がリセットされない場合があります。 - これは、Angular の
FormGroup
とFormGroupDirective
の役割分担によるものです。FormGroupDirective
が DOM と連携し、FormGroup
がその上に構築されています。
- そのため、フォーム送信後に
3. DOM イベントとフォーム送信の動作
- フォーム送信イベントの動作:
<form>
タグに対してsubmit
イベントが発生する際、ボタンやフィールド自体にはイベントが発生しません。これは、ブラウザのデフォルト動作に由来します。<button>
タグのデフォルトはtype="submit"
であり、novalidate
を付けないとフォーム送信が行われ、ネイティブのバリデーションが実行されます。バリデーションに失敗した場合、フォームは送信されず、submit
イベントも発生しません。
4. HTML の問題点
- エラーの原因:
<button>
タグのデフォルト動作により、フォームが送信され、ネイティブなバリデーションが実行されます。これにより、FormGroupDirective
で管理されている Angular のバリデーションとは別の動作が発生します。- これが、意図しないエラーメッセージやフォーム送信の原因となります。
5. エラーの解決策
- ベストプラクティス:
<form>
タグにnovalidate
を付け、ボタンにtype="button"
を指定することで、フォーム送信を防ぐことができます。<button mat-button type="button">ボタンラベル</button>
<input mat-button type="button" value="ボタンラベル">
- これにより、ネイティブの
submit
イベントによる不具合を回避できます。
6. 例外的なケースへの対応
- 特殊なケース: ネイティブの
submit/invalid
イベントサイクルを利用する場合は、NgForm
やFormGroupDirective
を利用してフォームの送信状態を管理する必要があります。- 具体的には、
NgForm
をコンポーネントに注入するか、FormGroupDirective
を使って DOM のフォームオブジェクトをresetForm()
でリセットします。これにより、DOM と Angular のフォーム状態が同期します。
- 具体的には、
まとめ
Angular でフォームのバリデーションと送信を適切に管理するためには、novalidate
の使用が推奨されます。これにより、ネイティブバリデーションによる予期せぬ動作を防ぎ、Angular のバリデーションロジックを優先させることができます。また、必要に応じて NgForm
や FormGroupDirective
を使ってフォームの状態を制御することが、最も確実な方法です。
この解説により、Angular フォームの内部で発生している複雑な相互作用と、それに伴う問題点をより深く理解できるでしょう。
novalidateはまあどうでもよくて、要は
- FormGroup は純粋な Angular の構造で、DOM API とは無関係
- なのでFormGroup#reset() を呼んでも、送信状態(submitted のフラグ)がリセットされない(場合があると書いてあるが実際にリセットされない)
- FormGroupDirective は DOM と連携してるので、こいつでresetすればブラウザ動作のresetがされる(要はtype="reset"と同じ動作)(ViewChildでやってもたぶんいけると思う)
なので、FormGroupDirective 使ってFormをリセットしたらちゃんと動作した