はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

エンプラ脳でAngularのReactiveFormsを使ってみる

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は複数あれど、それぞれの画面で共通してほしい機能は存在するはずです。

さくっと考えて下記な感じでしょうか。

  1. Validarionエラーチェック
  2. エラーメッセージ
  3. ページ遷移時の挙動
  4. データ確定処理

同一の機能であれば、これらを共通のロジックでまとめて作りたい!

で、複数のComponentでまとめて使いたい!

ということで、作ってみようと思います。

結果、下図のような動きになります。色々サボってるので動きがちょいアレですが…

f:id:TakasDev:20170614152820g:plain

Validartionエラーチェック

これは、Angularの公式リファレンスにかかれているとおりの機能ですね。

ただ、各Componentで共通した機能として作りたいので、ReactiveFormsの諸々の処理を実装したBaseClassを作成します。

各ComponentでBaseClassをExtendsしてValidationの処理を書かなくていい感じに仕上げます。

Angular v4 ReactiveFormのやつ

また、Validationの設定を全部ComponentClassの中に書いていくのも邪魔くさいので

ValidationのSetting部分を切り出します。

Angularv4ReactiveFormのやつ

作成するComponentでBaseComponentをExtendsして、上記で作成したValidarionのセッティングを読み込みます。

Angularv4ReactiveFormのやつ

BaseComponentで、ValidationCheckに必要な作業をほぼ行っているので

実際のComponentに記述する量はガクッと減るかと思います。

getterでValidationSettingのコントロール名を返しているのは、ComponentのHtml内で楽に使用するためです。

HTMLは下記のような作りになっています。

地味に、AngularMaterialを使用して実装しています。

gistf9b185b48afceca9c50a04637a200300

formControlのNameの定義部分が[formControlName]となっているのが地味な味噌ですね。

エラーメッセージを格納しているformErros等の指定も、上記のようにしています。

ValidationSettingで設定しているCONSTのキーワード値を変更すると、関連項目のすべてに適用されるため

「DBの項目名称と一致してなかったテヘペロ」って場合も、最小限の変更で適切な箇所に変更を波及されます。

エラーメッセージ

エラーメッセージについては、

「必須ですよ」とか「文字列Overですよ」とか一般的なエラー内容は集約して管理したいので

エラーメッセージ生成Classを作成しました。

Static宣言してnewなしで使えるようにしています。

Angularv4ReactiveFormのやつ

ページ遷移時の処理

入力中にページ遷移する場合、「いいの?」ってダイアログ出したい時があります。

そんな場合は、RouterのCanDeactivateを使用するのが良いですね。

下記の感じで作成しました。

Angularv4ReactiveFormのやつ

CanDeactivateを使用する際にGenericsにComponent型を突っ込みます。

その際、BaseComponentを継承して作成されたComponentと指定することで

Activate判断処理において、Component内の処理を呼び出せるようにします。

Componentの「canDeActivateInputPage」処理では、独自のダイアログをだすなり

何かの条件のときのみ確認するなり、よしなに処理を実装します。

今回はBaseComponentに処理を記述しましたが、継承先のComponentごとに実装をバラすこともできます。

Routerでは下記のように設定し、ページ遷移時に上記で作成したGuard処理が実行されるようにします。

Angularv4ReactiveFormのやつ

データ確定時の処理

初っ端開いた状態だと、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については

画面に設定する項目に関するものだけを変更すれば、新しい画面が比較的ラクに作れるようになります。

最後に

今回作成したソースは下記リポジトリで管理しています。

github.com