knockout-validationでBootstrapのform-groupにhas-errorを付ける のバックアップの現在との差分(No.3)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* knockout-validation の decorateInputElement [#y1323cd0]

[[knockoutjs>http://knockoutjs.com]] + [[knockout-validation>https://github.com/Knockout-Contrib/Knockout-Validation]] を使うと、フォームのフィールドへの入力値をリアルタイムに検証し、その結果をユーザーにフィードバックする動的フォームをかなり容易に作成できます。

特に、knockout-validation に decorateInputElement: true を与えると、エラーのある input に validationElement クラスを付与できるため、border-colorを変えるなどしてエラー箇所を分かりやすく表示できます。(このオプション、以前は decolateElement と呼ばれていたのですが、[[名前が変わったそうです>(https://github.com/Knockout-Contrib/Knockout-Validation/wiki/Configuration]])

* Bootstrap との組み合わせ [#e33bf77e]

ところがこれを [[Bootstrap>http://getbootstrap.com/]] と組み合わせようとすると、ちょっと困ったことになります。

なぜなら Bootstrap ではエラーのあるコントロールの表示を変えるのに input 自身ではなくそれを囲む div.form-group に has-error というクラスを指定しなければならないためです。

knockout-validation では errorElementClass というオプションで、エラーのあるコントロールに付与するクラス名を指定できますが、コントロールを囲む div.form-group にクラスを追加するという機能はないようです。

* そこで [#p94af267]

コントロールの parentNode をたどり、.form-group にマッチするものに has-error を追加するコードを以下のように書きました。
ひどい反則技ですが、コントロールの parentNode をたどり、.form-group にマッチするものに has-error を追加するコードを以下のように書きました。

 LANG:javascript(linenumber)
 // 要素の親のうち form-group であり、なおかつエラー要素を含むものに 
 // config.errorParentClass クラスを指定する
 var original_handler = ko.bindingHandlers.validationElement.update;
 ko.bindingHandlers.validationElement.update =
     function (element, valueAccessor, allBindingsAccessor) {
         original_handler(element, valueAccessor, allBindingsAccessor);
         var config = ko.validation.utils.getConfigOptions(element);
         var group = $(element).parents('.form-group');
         group.toggleClass(
             'has-error',
             group.find("."+config.errorElementClass).length > 0
         );
     };

1つの form-group に複数のコントロールが含まれる場合、1つでもエラーになるものがあれば has-error となるようにしてあります。

* jsfiddle で確かめられます [#g06a9315]

上記のコードは以下の jsfiddle で確かめられます。

http://jsfiddle.net/6B332/1/

* コントロールの削除に対応する [#c73a3932]

上記のコードに関して、エラーを含むコントロールが動的に削除された場合に、form-group に has-error が残ってしまう不具合があることに気付きました。

この問題を解消する単純な方法として、エラーを含む可能性のあるコントロールを削除した後に、すべての .form-group に対して toggleClass を呼んでしまう手もあるのですが・・・もっと賢い方法がないか考えているところです。

 LANG:javascript(linenumber)
 function updateAllFormGroups(errorElementClass=null)
 {
     if(errorElementClass == null)
         errorElementClass =
             ko.validation.configuration.errorElementClass;
     $('.form-group').each(function(){
         var group = $(this);
         group.toggleClass(
             'has-error',
             group.find("." + errorElementClass).length > 0
         );
     });
 }

* errorElementClass の扱いについて [#r19c8c23]

上記 bindingHandlers を書き換えているコードと、updateAllFormGroups のコードとを比べると、errorElementClass の扱いが異なることに気付きます。

これらはそのどちらも 、本来期待される動作と異なることに注意が必要です。

前提として、knockout-validation では、ko.applyBindingsWithValidation(viewModel, rootNode, /*options object */ ); の形で errorElementClass の値をルートノード毎に切換えて指定できます。あるいは、data-bind="validationOptions: /*options object */" という記述により任意の場所で切換えることもできます。~
https://github.com/Knockout-Contrib/Knockout-Validation/wiki/Configuration

したがって、updateAllFormGroups のように単に ko.validation.configuration.errorElementClass を読んだのでは、正しい errorElementClass を知ることができない可能性があります。errorElementClass 名を別途指定可能にしてあるのはこのためです。

bindingHandlers を書き換えているコードでは ko.validation.utils.getConfigOptions(element); としているため、一見切換えにも対応できているように思えますが、1つの form-group の中でコンテキストを切換えて、異なる errorElementClass を使っているとうまく行かないはずです。こちらが問題になることはほとんど無いとは思いますが・・・

(本来の ViewModel の考え方からすれば、複数のコントロールを含む form-group にエラーを表示するならそれに対応する model を用意すればいいわけですが、ここではそういう話はしない方向で)

* この記事は Qiita にも投稿しました [#i7f884cc]

http://qiita.com/osamu_takeuchi/items/ffa06281676f60d14973

* コメント [#w297d210]

#article_kcaptcha



Counter: 7122 (from 2010/06/03), today: 3, yesterday: 0