この記事は、knockout.js Advent Calendar 2015の8日目の記事です。 先に7日目に目を通すことを推奨しています。
knockout , knockout-es5 , knockout.punches環境を想定しています。
knockoutにも、scope(context)の概念があります。
scopeが切り替わるタイミングは、foreach
bindなどのイテレータでの子scopeや、with
bindでの明示的なscope切換えを行った場合です。
例えば、このサンプルを見てみましょう。
Country(国)とState(州/県)とCity(市・区)はそれぞれ親子関係にあります。
<div data-bind="foreach:countries">
<div class="country">
<span>{{name}}</span>
{{#foreach: states}}
<div class="state">
<span>{{name}}</span>
{{#foreach: cities}}
<div class="city">
<span>{{name}}</span>は、{{$parents[1].name}}の{{$parents[0].name}}にあります
</div>
{{/foreach}}
</div>
{{/foreach}}
</div>
</div>
<div data-bind="foreach:list">
は、divの内側を繰り返し、
{{#foreach:list}} {{/foreach}}
もまた、このタグの内側を繰り返します。
これは、<!-- ko foreach:list--><!-- /ko -->
の構文糖衣です。
VirtualElementと呼ばれるもので、余計なタグのネストを避けるために利用しています。
(コメントを用いる方法を使ってもいいのですが、すこし長いですし、最適化ツールなどでコメントが消された場合に、不都合が生じるので注意が必要です。)
サンプルを眺めてみると、なんとなくわかる通り、
div.countryの内側が指す name は国名ですし、
div.stateの内側側す name は 県・州名です。
div.cityでは name は市・区名となっています。
子scopeから親scopeを参照するには、 $parents
を使います。
$parents[0]
と$parent
は等価です。
cityのscopeからみて、
$parents[0]はstateのscopeで、
$parents[1]はcountryのscope
となっています。
function Country(name){
this.states=[];
this.name = name;
ko.track(this);
}
function State(name){
this.cities=[];
this.name = name;
ko.track(this);
}
function City(name){
this.name = name;
ko.track(this);
}
function VM(){
var countries = this.countries = [];
var src = [
{
name:'jp',
state:[
{name:'東京',city:[{name:'千代田'},{name:'港'},{name:' 渋谷'}]},
{name:'大阪',city:[{name:'大阪'},{name:'堺'},{name:'松原'}]},
{name:'宮城',city:[{name:'仙台'},{name:'角田'},{name:'名取'}]}
]
},
{
name:'us',
state:[
{name:'Texas',city:[{name:'SanAntonio'},{name:'Austin'},{name:' Huston'}]},
{name:'NewYork',city:[{name:'Rochester'},{name:'Buffalo'}]}
]
}
];
// mapping to model
src.forEach(function(c){
var country = new Country(c.name);
c.state.forEach(function(s){
var state = new State(s.name);
s.city.forEach(function(ci){
state.cities.push(new City(ci.name));
});
country.states.push(state)
});
countries.push(country);
});
ko.track(this);
}
var vm = new VM();
ko.punches.enableAll();
ko.applyBindings(vm);
scopeのcontextには、
$parent
や$parents[]
や$data
そして、イテレータのscopeでは$index
を持っています。
それぞれ、
$parent
は、自分の親scope、
$parents
は、自分の先祖scope、
$data
は、自分自身のscope、
$index
は、親から見た自分のindex
となります。
$data
の使い道は、例えば data-bind="foreach:['A','B','C']"
など、propertyを有さないprimitiveな値をまわす際などに使ったりします。