AngularJSでフォームを入れ子にしたときに内側のフォームを非表示にしてバリデーションをスキップする術
こんにちは。ワインをたくさん飲みました。きたけーです。
AngularJSでフォームを入れ子にしたときの内側のフォームのバリデーションをスキップする術を知ったのでメモします。
HTMLのタグの仕様では、formタグはネストできないことになっていますが※1、
AngularJSのng-formディレクティブはネストできます ※2。
※2に書かれているように外側のフォームがvalidということは内側のフォームが全てvalidということです。
入力項目が多いフォームでは、入力項目のまとまりごとにバリデーションをおこなうためにフォームを分割してネストさせますが、入力された内容によっては内側のフォームを非表示にして、そのフォームのバリデーションをスキップさせたいときがあります。
そのような場合は以下のようにng-if
ディレクティブで表示・非表示を切り替えることで実現できます。
こんなかんじです。
<form name="alphaForm" novalidate> <input type="text" placeholder="Name" name="name" ng-model="user.name" required /> <div ng-messages="alphaForm.name.$error" ng-if="alphaForm.$dirty"> <div ng-message="required">入力してください</div> </div> <ng-form name="betaForm" ng-if="somethingCondition()"> <input type="text" placeholder="Age" name="nage" ng-model="user.age" /> <div ng-messages="betaForm.age.$error" ng-if="betaForm.$dirty"> <div ng-message="required">入力してください</div> </div> </ng-form> <input type="submit" ng-disabled="alphaForm.$invalid">Submit</input> </form>
入れ子になっているbetaFormをng-ifディレクティブで制御しています。somethingConditionはスコープに生えた真偽値を返す適当なメソッドです。
このとき、somethingConditionが偽を返せば、betaFormは非表示になり、名前だけ入力すれば、alphaFormのバリデーションが通り、submitボタンを押すことができます。
逆にsomethingConditionが真を返せば、betaFormが表示され、年齢も入力しなければ、alphaFormのバリデーションは通らないので、submitボタンが無効になり、押せません。
単純に表示・非表示でバリデーションをスキップするかどうか切り替えれるわけではなく、ng-if
ディレクティブの代わりにng-show
ディレクティブを使うと、somethingConditionが偽のときに、名前を入力してもバリデーションは通りません。
ng-if
とng-show
の違いは表示・非表示をHTMLの要素レベルでおこなうか、スタイルシートのプロパティレベルでおこなうかですが、この挙動の違いは追々、Angular本体のFormController, NgModelControllerコードを読んで調べてみようと思います。
※1 フォームコントロールはform要素内に書きましょう - Web標準普及プロジェクト
またform要素の閉じタグは必須ですし、form要素の入れ子(ネスト)は禁止されています。
※2 https://docs.angularjs.org/api/ng/directive/form
In Angular forms can be nested. This means that the outer form is valid when all of the child forms are valid as well.