Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

knockoutのscope(context)

More than 5 years have passed since last update.
この記事は、knockout.js Advent Calendar 2015の8日目の記事です。 先に7日目に目を通すことを推奨しています。
knockout , knockout-es5 , knockout.punches環境を想定しています。

knockoutにも、scope(context)の概念があります。
scopeが切り替わるタイミングは、foreachbindなどのイテレータでの子scopeや、withbindでの明示的なscope切換えを行った場合です。

例えば、このサンプルを見てみましょう。
Country(国)とState(州/県)とCity(市・区)はそれぞれ親子関係にあります。

index.html
<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
となっています。

script.js
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な値をまわす際などに使ったりします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away