knockout.jsとTwitter Bootstrapの相性
knockout.js(以下KO とTwitter Bootstrapを併用していて、良い面もあるのですが、動かなくなるケースがいくらかあります。
主にBootstrapのDocument内にあるjavascriptの一覧に載っているものにKOでバインドするとclickなどのevent絡みでどうも動かない時がある。なぜなんだろうといろいろconsole.logしてみました。
コード例
<div class="btn-group" data-toggle="buttons" id="toggleBtn" data-bind="foreach: btnList"> <label class="btn btn-default" data-bind="click: $root.clickEvnt" > <input type="checkbox" data-bind="value: id,checked: $root.checkedList" /><span data-bind="text: val">1</span> </label> </div> <script type="text/javascript"> $(function() { function ViewModel() { var self = this; self.btnList = [ {id: 1, val:1}, {id: 2, val:2}, {id: 3, val:3} ]; self.clickEvnt = fuction(data, event){}; self.checkedList = ko.observableArray(["2"]); } vm = new ViewModel(); ko.applyBindings(vm); }); </script>
上記のソースで、Bootstrapのtoggleボタンにバインドした時の思うようにいかない例です。
- KOでcheckedをバインドしていてるのですが、toggleボタンの初期状態が反映されない
- クリック時には、checkedList内の値が更新されない
- その割にはlabelにバインドしたclickEvnt()は効いてる
- ちなみにlabelにclickEvnt()をバインドしなくても同じ
結局これらは、バッティングして両者ともに途中で処理が中断しているわけではありませんでした。KOもBootstrapもそれぞれ正常に動いています。
KOでcheckedしていてもlabelのclassに'active'を追加しないと押された状態に見えていなかった。
Bootstrapのマニュアルどおり初期化でクラスを追加してください。ということで、KOの初期化が終わったらjQueryでinputのcheckedを見てaddClassで'active'にして解決。
後はクリック時Bootstrapが通常どおり判断してくれます。
KOでバインドしていたはずのobservableArrayに反映されないのは、KO通してinputを変えているわけではない。
Bootstrap側で直接inputを変えているみたいで、inputElementへはちゃんと反映されてます。やってることに気づいたらそりゃそうだよねという話。
ですが、これをどうやって、ViewModelへ反映させるか?困っていたのですが。結局、jQueryで$(element).on('change', function(){})を使って値をjQueryでさらいまとめてVMのobserbableArrayへ入れるイベントを登録しました。
解決方法がスマートではなく、対症療法でしたが、jQuery使えばそんなに手間でもないわけで、両方とも使わないよりはわりと楽なんですよね。