AngularのReactiveFormsは素敵なFormModuleだと思います。
自分も実際の作業の際には非常にお世話になりました。
ReactiveFormsについて
https://angular.io/docs/ts/latest/guide/reactive-forms.html
https://angular.io/docs/ts/latest/cookbook/form-validation.html
上記のAngularの公式サイトが大変参考になります。
基本のキは、上記サイトで学習できるので、エンプラ脳でReactiveFormsの利用方法を考えてみようと思います。
記事作成当時のAngularはV4です。
あと、これベストプラクティスかどうかはかなり怪しいので、色々ご意見いただけるとありがたいです。
Formでほしい機能を考える
さて、エンプラな頭で考えたとき、WebApplicationは同じような機能の複数の入力Formを持つかとおもいます。
入力Formは複数あれど、それぞれの画面で共通してほしい機能は存在するはずです。
さくっと考えて下記な感じでしょうか。
- Validarionエラーチェック
- エラーメッセージ
- ページ遷移時の挙動
- データ確定処理
同一の機能であれば、これらを共通のロジックでまとめて作りたい!
で、複数のComponentでまとめて使いたい!
ということで、作ってみようと思います。
結果、下図のような動きになります。色々サボってるので動きがちょいアレですが…
Validartionエラーチェック
これは、Angularの公式リファレンスにかかれているとおりの機能ですね。
ただ、各Componentで共通した機能として作りたいので、ReactiveFormsの諸々の処理を実装したBaseClassを作成します。
各ComponentでBaseClassをExtendsしてValidationの処理を書かなくていい感じに仕上げます。
また、Validationの設定を全部ComponentClassの中に書いていくのも邪魔くさいので
ValidationのSetting部分を切り出します。
作成するComponentでBaseComponentをExtendsして、上記で作成したValidarionのセッティングを読み込みます。
BaseComponentで、ValidationCheckに必要な作業をほぼ行っているので
実際のComponentに記述する量はガクッと減るかと思います。
getterでValidationSettingのコントロール名を返しているのは、ComponentのHtml内で楽に使用するためです。
HTMLは下記のような作りになっています。
地味に、AngularMaterialを使用して実装しています。
gistf9b185b48afceca9c50a04637a200300
formControlのNameの定義部分が[formControlName]となっているのが地味な味噌ですね。
エラーメッセージを格納しているformErros等の指定も、上記のようにしています。
ValidationSettingで設定しているCONSTのキーワード値を変更すると、関連項目のすべてに適用されるため
「DBの項目名称と一致してなかったテヘペロ」って場合も、最小限の変更で適切な箇所に変更を波及されます。
エラーメッセージ
エラーメッセージについては、
「必須ですよ」とか「文字列Overですよ」とか一般的なエラー内容は集約して管理したいので
エラーメッセージ生成Classを作成しました。
Static宣言してnewなしで使えるようにしています。
ページ遷移時の処理
入力中にページ遷移する場合、「いいの?」ってダイアログ出したい時があります。
そんな場合は、RouterのCanDeactivateを使用するのが良いですね。
下記の感じで作成しました。
CanDeactivateを使用する際にGenerics
その際、BaseComponentを継承して作成されたComponentと指定することで
Activate判断処理において、Component内の処理を呼び出せるようにします。
Componentの「canDeActivateInputPage」処理では、独自のダイアログをだすなり
何かの条件のときのみ確認するなり、よしなに処理を実装します。
今回はBaseComponentに処理を記述しましたが、継承先のComponentごとに実装をバラすこともできます。
Routerでは下記のように設定し、ページ遷移時に上記で作成したGuard処理が実行されるようにします。
データ確定時の処理
初っ端開いた状態だと、formはCleanな状態です。
Submitが走るとエラー状態にはなるものの
ValueChangeイベントが走らないため、FormsErrorsにエラー内容が格納されません。
なので、内部の全項目をDirty状態にして、ValidationCheckをあえて走らせる処理を実装します。
上記BaseComponentの↓の実装部分ですね。
allControlReCheck() { for (const field in this.formErrors) { const control = this.inputForm.controls[field]; control.markAsDirty(); this.onValueChange(); } }
あとは複製…
ValidationSettingとHtmlとComponentについては
画面に設定する項目に関するものだけを変更すれば、新しい画面が比較的ラクに作れるようになります。
最後に
今回作成したソースは下記リポジトリで管理しています。