AngularのngModelが非推奨になっていたためFormGroup
に仕様を変更してみた
はじめに
みなさんこんにちは!
株式会社ネクストビートに新卒入社した清水琢巳と申します。
ネクストビートエンジニア研修を受けている中で、Angularのフォームを作るために使用していたngModel
が非推奨になっているのに気づき、調べたところ現在はリアクティブフォームを実現できるFormGroup
を使用するのが一般的みたいだったので、FormGroupを使用して改修いたしました。今回はその改修の流れと、その中で自分が気づいたことを紹介していきたいと思います!!
今回作るもの
こんなやつ作ります。
ngModelを使用した実装
以下が今回解説のために使用するngModelを使用したサンプルコードになります。
※今回はリアクティブフォームに注目したいため、categoryやcolorなどの具体的なコードまでは、触れません。
ngModelとは
現在使用しているngModelは双方向バインディングというform
の値が変わると変数名が変わり変数名が変わるとform
の値も変わる形で実装しています。
ngModel
はinput
タグに[(ngModel)] = "変数名"
を付与することでinput
の入力値と変数の値紐付けることができます。
input
タグのname
は変数名と同じかわかりやすい名前にしておくことをお薦めいたします。
FormControl / FromGroupを使用した実装
FormControl / FromGroupとは
ここまではngModel
についての話だったのですが、非推奨になってしまっていたため、FormGroup
を用いて先ほどのform
の仕様を変更していきます。
FormControl
はひとつの入力フィールドに相当し、Angular form
の最も小さいユニットです。FormControl
は入力フィールドの値をカプセル化し、その値をvalid
(正しい)dirty
(変更された)error
(エラー)の3種類で返してくれます。
FormGroup
はform
内のたくさんある管理や判断しなければならないフィールド(FromControl
)をまとめて値や状態をチェックしてくれます。つまりFormGroup
は複数のFromControl
をまとめて管理できます。
FromGroupへ仕様変更・初期値割り当てまで
ここからはngModel
を使用した実装をFormGroup
を使用した形に変更した過程を紹介します。まずは初期値を割り当てるところまでです。
以下のcategory-detail.component.ts
内で使用する先ほど説明したFormControl
とFormGroup
をimport
して変数を定義した後に初期値を割り当てるメソッドを実装します。
それでは以上をhtml
に反映させていきます。
本項で行ったことは以下の3つです。
categoryEditForm: FormGroup
を定義します。category
の値を割り当てるメソッドを定義しngOnInit
内でcategory
の値取得後に呼び出しています。- それぞれの
input
タグにformControlName
を割り当て初期値を紐づけるています。その時にname
とformControlName
を同じにします。(同じにしない場合Cannot find control with name: ‘〇〇’
というエラーで怒られることになります。)
FromGroupに修正(リアクティブフォームまで)
それでは上記のフォームをリアクティブフォームに変換し直していきます。
category-detail.component.ts
の末尾に以下を追加します。
以上のメソッドはFormGroup
で入力 や選択された値を取得して返してくれます。
次に今作成した3つのメソッドをhtml
で表示するように変更します。
本項で行ったことは以下の2つです。
category
取得後categoryEditForm
の値が空だった時のみcategory
の値を初期値にset
するようにngIf
を変更しています。FormGroup
の値を取得して返すメソッドを作成し、それぞれの値を表示するように変更しています。
FromGroupへ仕様変更(変更データ送信メソッドの修正)
最後に以上の修正によりsave
メソッドも以下のように変更を加えます。
以上でリアクティブフォームへ修正することができました。しかし、console
を開いてみると以下のエラー文が表示されていると思います。
ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.. Find more at https://angular.io/errors/NG0100
at throwErrorIfNoChangesMode (core.mjs:6735)
at bindingUpdated (core.mjs:12735)
at Module.ɵɵproperty (core.mjs:14468)
at CategoryDetailComponent_Template (category-detail.component.html:1)
at executeTemplate (core.mjs:9620)
at refreshView (core.mjs:9486)
at refreshComponent (core.mjs:10680)
at refreshChildComponents (core.mjs:9282)
at refreshView (core.mjs:9536)
at refreshEmbeddedViews (core.mjs:10634)
調べてみたところ上記エラーは以下が原因との記載がありました。
変更検知が完了した後に式の値が変更された場合、Angular は ExpressionChangedAfterItHasBeenCheckedError をスローします。 Angularは開発モードでのみこのエラーをスローします。[2]
開発モードのみでのエラーのようですが、エラーが出るのは嬉しくないので、ChangeDetectorRef
を使用して再読み込みをかける方法でエラーをなくしていきます。
以下を追加することで再読み込みがかかりエラーはなくなります。
おまけのValidators
FormControl
FormGroup
の強みであるValidators
について軽く触れて、まとめに入りたいと思います。
まずValidators
は
ユーザー入力の正確性と完全性を検証することで、全体的なデータ品質を向上させることができます。[3]
と言われてもピンとこないかもしれませんが、今回のFormGroup
では、作成したフィールド一つ一つに検証するメソッド等をつけて管理することができると言い換えることができます。
早速今回のFormGroup
のフィールドを必須項目にして、入力されなかった場合にエラーメッセージを表示するように変更していきたいと思います。
以上により変更が入力されていなかった場合エラーを表示し、エラー時はsubmit
ボタンも押すことができなくなるようになりました。
まとめ
ngModel
とFormGroup
を比較する表を作成してみました。
今回ngModel
を使用したform
を、FormGroup
を使用した形に修正したことで、ngModel
はとても簡単に実装することができますが、FormControl
やFormGroup
でできるような高度なことは実装することが難しいことと、両者を比べるとやはり、FormGroup
の方が安全性が高いという点から、ngModel
が非推奨になったのではないかという自分なりの結論を導くことができました。
また私事ではありますが、研修を受けていく中でこの調査は、素朴な疑問に向き合い深堀していく楽しさを知る良い機会となりました。
まだまだ半人前ではございますが、爆速でレベルを上げるために小さな疑問も疑問のままにせずとことん調査し理解して進んでいきたいと思います。
最後まで見ていただきありがとうございました。
<参考>
[1] https://angular.jp/guide/reactive-forms
[2] https://angular.jp/errors/NG0100
[3] https://angular.jp/guide/form-validation
[4] https://angular.jp/api/core/ChangeDetectorRef
We are Hiring!
「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。
バックエンドにはPlay Framework(言語はScala)、フロントエンドの開発には主にAngularを用いています。フルスタックに開発したい!という方のご応募をお待ちしております。