AngularのngModelが非推奨になっていたためFormGroupに仕様を変更してみた

Takumi Shimizu
nextbeat-engineering
8 min readJul 25, 2022

--

はじめに

みなさんこんにちは!

株式会社ネクストビートに新卒入社した清水琢巳と申します。

ネクストビートエンジニア研修を受けている中で、Angularのフォームを作るために使用していたngModelが非推奨になっているのに気づき、調べたところ現在はリアクティブフォームを実現できるFormGroupを使用するのが一般的みたいだったので、FormGroupを使用して改修いたしました。今回はその改修の流れと、その中で自分が気づいたことを紹介していきたいと思います!!

今回作るもの

こんなやつ作ります。

ngModelを使用した実装

以下が今回解説のために使用するngModelを使用したサンプルコードになります。

※今回はリアクティブフォームに注目したいため、categoryやcolorなどの具体的なコードまでは、触れません。

ngModelとは

現在使用しているngModelは双方向バインディングというformの値が変わると変数名が変わり変数名が変わるとformの値も変わる形で実装しています。

ngModelinputタグに[(ngModel)] = "変数名"を付与することでinputの入力値と変数の値紐付けることができます。

inputタグのnameは変数名と同じかわかりやすい名前にしておくことをお薦めいたします。

FormControl / FromGroupを使用した実装

FormControl / FromGroupとは

ここまではngModelについての話だったのですが、非推奨になってしまっていたため、FormGroupを用いて先ほどのformの仕様を変更していきます。

FormControlはひとつの入力フィールドに相当し、Angular formの最も小さいユニットです。FormControlは入力フィールドの値をカプセル化し、その値をvalid(正しい)dirty(変更された)error(エラー)の3種類で返してくれます。

FormGroupform内のたくさんある管理や判断しなければならないフィールド(FromControl)をまとめて値や状態をチェックしてくれます。つまりFormGroupは複数のFromControl をまとめて管理できます。

FromGroupへ仕様変更・初期値割り当てまで

ここからはngModelを使用した実装をFormGroupを使用した形に変更した過程を紹介します。まずは初期値を割り当てるところまでです。

以下のcategory-detail.component.ts 内で使用する先ほど説明したFormControlFormGroupimportして変数を定義した後に初期値を割り当てるメソッドを実装します。

それでは以上をhtml に反映させていきます。

本項で行ったことは以下の3つです。

  1. categoryEditForm: FormGroupを定義します。
  2. categoryの値を割り当てるメソッドを定義しngOnInit内でcategoryの値取得後に呼び出しています。
  3. それぞれのinputタグにformControlNameを割り当て初期値を紐づけるています。その時にnameformControlNameを同じにします。(同じにしない場合Cannot find control with name: ‘〇〇’というエラーで怒られることになります。)

FromGroupに修正(リアクティブフォームまで)

それでは上記のフォームをリアクティブフォームに変換し直していきます。

category-detail.component.tsの末尾に以下を追加します。

以上のメソッドはFormGroupで入力 や選択された値を取得して返してくれます。

次に今作成した3つのメソッドをhtmlで表示するように変更します。

本項で行ったことは以下の2つです。

  1. category取得後categoryEditFormの値が空だった時のみcategoryの値を初期値にsetするようにngIf を変更しています。
  2. 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ボタンも押すことができなくなるようになりました。

まとめ

ngModelFormGroup を比較する表を作成してみました。

今回ngModelを使用したformを、FormGroupを使用した形に修正したことで、ngModelはとても簡単に実装することができますが、FormControlFormGroupでできるような高度なことは実装することが難しいことと、両者を比べるとやはり、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を用いています。フルスタックに開発したい!という方のご応募をお待ちしております。

--

--