knockout.js バインディング式内で、 $data
、$parent
、および$root
疑似変数 を使用できます。 JavaScriptで宣言された ko.computed
observable を使用しているときに、これらの擬似変数に相当するものを取得するにはどうすればよいですか?
子のコレクションを持つ親ビューモデルがあり、親ビューモデルにはselectedChild
オブザーバブルがあります。そのため、データバインディング式を使用して、現在選択されている子にCSSクラスを追加できます。
<ul data-bind="foreach: children">
<li data-bind="text: name,
css: {selected: $data === $root.selectedChild()},
click: $root.selectChild"></li>
</ul>
<script>
vm = {
selectedChild: ko.observable(),
children: [{name: 'Bob'}, {name: 'Ned'}],
selectChild: function(child) { vm.selectedChild(child); }
};
ko.applyBindings(vm);
</script>
しかし、私のビューモデルはより複雑になり、「私は選択しますか?」単一のCSSクラスを単一の要素に追加するだけではありません。子ビューモデルでisSelected
計算プロパティを作成したいので、それに依存する他の計算プロパティを追加できます。
ノックアウトがこれらの変数を定義し、computed
エバリュエーターを呼び出すときにスコープ内にある可能性があるという偶然に、$data
および$root
を参照するJavaScriptを記述しようとしました。関数:
{
name: 'Bob',
isSelected: ko.computed(function(){ return $data === $root.selectedChild(); })
}
しかし、そのような運はありません。私の評価者function
の中では、$data
と$root
の両方がundefined
です。
また、エバリュエーター内で ko.contextFor
を使用しようとしました。これは、$data
および$root
へのアクセスを許可するためです。残念ながら、評価関数内では、contextFor
も常にundefined
を返します。 (とにかくこの戦略に大きな期待を抱きませんでした-このように後ろに行かなければならないとしたら、ノックアウトが依存関係をどれだけうまく追跡できるかは明確ではありません。)
親ビューモデルを参照する子ビューモデルごとに、常に手動でプロパティを設定できました。しかし、ノックアウトにはこれを行う能力があることを知っているので、自分で書く前に、少なくともそのメカニズムを使用できるかどうかを調べたいと思います。
上記のバインディング式を計算されたオブザーバブルに変換することが可能であるように思われます-結局のところ それはすでにノックアウトが行っていることです :
もう1つの巧妙なトリックは、宣言型バインディングが計算されたオブザーバブルとして単純に実装されることです。
しかし、自分で計算されたオブザーバブルを記述しているときに、$data
および$root
疑似変数をどのように扱うのでしょうか。
疑似変数は、データバインディングのコンテキストでのみ使用できます。ビューモデル自体は、理想的には、それを表示しているビューについて認識していないか、依存しているべきではありません。
そのため、計算されたオブザーバブルをビューモデルに追加する場合、バインドされる方法($ rootになるものなど)がわかりません。ビューモデルまたはビューモデルの一部は、異なるレベルでページの複数の領域に個別にバインドすることもできるため、擬似変数は開始する要素によって異なります。
それは何を達成しようとしているかに依存しますが、このアイテムが親ビューモデルで選択されたアイテムと同じかどうかを示すisSelected
計算されたオブザーバブルを子供に持たせたい場合は、親を子が利用できるようにする方法を見つけます。
1つのオプションは、親を子のコンストラクター関数に渡すことです。ポインタを子のプロパティとして親に追加する必要さえなく、計算されたオブザーバブルで直接使用できます。
何かのようなもの:
var Item = function(name, parent) {
this.name = ko.observable(name);
this.isSelected = ko.computed(function() {
return this === parent.selectedItem();
}, this);
};
var ViewModel = function() {
this.selectedItem = ko.observable();
this.items = ko.observableArray([
new Item("one", this),
new Item("two", this),
new Item("three", this)
]);
};
ここのサンプル: http://jsfiddle.net/rniemeyer/BuH7N/
選択したステータスだけが必要な場合は、それを調整して、次のようなselectedItem
observableへの参照を子コンストラクターに渡すことができます。 http://jsfiddle.net/rniemeyer/R5MtC/
親ビューモデルがグローバル変数に格納されている場合、次のように子に渡さずに直接使用することを検討できます。 http://jsfiddle.net/rniemeyer/3drUL/ 。ただし、参照を子供に渡すことを好みます。
私の経験では、Item
sがアプリケーションの存続期間中に存在する場合、@ RP Niemeyerの答えのアプローチは問題ありません。しかし、そうでない場合、Item
の計算されたobservableはViewModel
から逆の依存関係を設定するため、メモリリークにつながる可能性があります。繰り返しになりますが、Item
オブジェクトをまったく削除しなくてもかまいません。ただし、Item
sを削除しようとしても、ノックアウトにはその逆の依存関係参照が残るため、ガベージコレクションは行われません。
あなたはcould、おそらくItem
のcleanup()メソッドで計算されたものを破棄することを確認しますが、アイテムがなくなると呼び出されますが、 Item
sを削除するときは、必ずそれを行う必要があります。
代わりに、Item
をもう少しスマートにし、ViewModel
が選択されたときに通知するようにしないのはなぜですか? Item
のisSelected()
を通常の古いオブザーバブルにしてから、ViewModel
でselectedItem
をサブスクライブし、そのサブスクリプション内で更新するだけです。
または、@ RP Niemeyerの pub/sub solution を使用します。 (公平を期すために、この解決策はここでの彼の答えの後に生まれました。)ただし、逆の依存関係も作成するため、クリーンアップする必要があります。しかし、少なくとも結合は少なくなります。
詳細については、この同じトピックの 私の最近の質問 への回答を参照してください。