🥊

Knockout.jsを使っていてハマった事

2021/08/11に公開

はじめに

Knockout.js 3.3.0 を使い始めて数週間ですが、
とりあえずハマった事をメモします。

Mapping

JSON から Knockout の Observable を作ってくれる便利なプラグインですが…

ko.toJSON するといらないものが付く

ko_mappings みたいなのが付いてしまう。

→ ko.mapping.toJSON を使う必要がある。

Editables

編集のロールバックや、変更されたか否かを自動で判定してくれる便利なもの。

ko.editable(hoge) すると hasChanges が追加されてしまう

ko.mapping.toJSON したときに余計なプロパティが

付いてしまう。(で、例えば Java とか型の固い言語に JSON 化して引き渡すと hasChanges を

解釈できずにコケる)

→ どうにもならないので、ko.mapping 側の ignore に追加する。hasChanges という名前をフィールドに使えなくなるが仕方が無い(editable 使う時点で使えないし)

http://knockoutjs.com/documentation/plugins-mapping.html

ko.mapping.defaultOptions().ignore = ["hasChanges"];

observableArray に subscribe したは良いけど、変更が通知されない

→FAQ らしい。observableArray は中身について一切関知しない。

なので、追加/削除の時しか通知は来ない。

see: http://kojs.sukobuto.com/docs/observableArrays

・・・でもこれ、直感的な動作とは言いがたいような気がする。

じゃあ中身の変更を知りたい時は?

ObservableArray の中身のプロパティを、ko.observable で定義して(ko.mappings 使えば自動)

Array 内の全てのオブジェクトの、必要なプロパティに Subscribe すれば良い。

Subscribe するときは、次の項目の通り、 this に束縛する値に都合のよいものを指定する。

Subscribe したは良いけど、変更されたオブジェクトがハンドラに渡ってこない!

Subscribe するときの第二引数で、ハンドラの this に束縛する値をセットする必要がある。

Subscribe をやめたい

Subscribe した時に返ってくるオブジェクト.dispose() すれば OK。

ko.utils.arrayForEach(array.items(), function (item) {
	var subscribe = item.value.subscribe(function (newValue) {
		// 変更されたときのハンドラ
	}, item);     // 第二引数にちゃんと対象オブジェクトを指定しておく

	subscribe.dispose();  // subscribeやめる
});

observableDictionary が Subscribe できない

実装されてないのでどうにもならない模様。私は諦めた。

ただし、中身は普通に Observable なはずなので、subscribe できるはず。

$root は出来るだけ使わない

ViewModel 一個で画面を作っている場合、$root = $parent となるのが大半でしょう。

この時、できるだけ $parent で指定しておいた方が良いです。

後々、画面が複雑化してきて、複数 ViewModel に分割した場合(以下例参照)

    // 例えばこんな感じです
    var viewModel = new MainViewModel();
    var subModel = new SubViewModel();
    ko.applyBindings({
        main: viewModel
        , sub: subModel
    });

    // HTML内
    <div id="main" data-bind="with: main">
    略
    </div>
    <div id="sub" data-bind="with:sub">
    略略
    </div>

元々$root で取得していたものは $root.main で参照するようになります。

rootを使っている箇所、全て書き直しになってしまうので、parent を

できるだけ使う事をおすすめします。というより、$root を使わないといけないことは

滅多にないはずです。

→2015/08/30 追記

そもそも、大きく囲んだ要素に data-bind="with: main" と書いてあるのであれば

main の viewModel の中の要素を指定する際、プレフィックス的なものは不要。

とりあえず data-bind="with:hoge" を書いておいた方が収まりがいいと感じた。

バインディングコンテキストは理解しておいた方がよさそう。

-> 2015/09/30 追記

viewModel が一つしかない場合でも、ko.applyBindings(viewModel); は使わずに

上のコードのように連想配列を渡しておけば、後からVM分割したくなっても安心。

可能であれば if より visible を使う

if は、条件が false な時に、その中の要素を本当に消してしまいます。

条件が成立すると、中の要素を追加します。何が問題かというとバインドした

イベントが消えるということです。

visible であれば隠れるだけなのでこういった問題は発生しません。

Discussion