くわこのpermission denied.

WEBエンジニアの僕がぶつかった技術的な問題や発見

KnockoutJsの$root,$parent(s)とは?

Knockoutjsの$root(あるいは$parent(s))とは?

knockout.jsのチュートリアル(
learn.knockoutjs.com
)やってて途中から急に$root(あるいは$parent(s))って変数が出てくるようになって、なんやねん( ´_ゝ`)と思ったので共有。

View側

<table>
    <thead><tr>
        <th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
    </tr></thead>
    <!-- Todo: Generate table body -->
    <tbody data-bind="foreach: seats">
        <tr>
            <td><input data-bind="value: name" /></td>
            <td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
            <td data-bind="text: formattedPrice"></td>
            <td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
        </tr>   
    </tbody>
</table>

ViewModel側

// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
    var self = this;
    self.name = name;
    self.meal = ko.observable(initialMeal);
    
    self.formattedPrice = ko.computed(function() {
        var price = self.meal().price;
        return price ? "$" + price.toFixed(2) : "None";
    });
}

// Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
    var self = this;

    // Non-editable catalog data - would come from the server
    self.availableMeals = [
        { mealName: "Standard (sandwich)", price: 0 },
        { mealName: "Premium (lobster)", price: 34.95 },
        { mealName: "Ultimate (whole zebra)", price: 290 }
    ];    

    // Editable data
    self.seats = ko.observableArray([
        new SeatReservation("Steve", self.availableMeals[0]),
        new SeatReservation("Bert", self.availableMeals[0])
    ]);

この中の↓ここ

<select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'">

なんでmealに関しては value: mealでできてるのに、availableMealsはoption: $root.availableMealsみたいに$rootつけなきゃいけないねん( ´_ゝ`)( ´_ゝ`)( ´_ゝ`)

チュートリアルに$rootの説明っぽいの

Note that the $root. prefix causes Knockout to look for a removeSeat handler on your top-level viewmodel instead of on the SeatReservation instance being bound --- that's a more convenient place to put removeSeat in this example. So, add a corresponding removeSeat function to your root viewmodel class, ReservationsViewModel:

あったけど、如何せんEnglishでピンと来ない(・◡ु‹ ) ✧*テヘペロ

ってわけで色々調べて、結局先輩に聞いて解決笑


要は変数のスコープ的な問題らしい。

<select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'">

↑を使ってるときって

<tbody data-bind="foreach: seats">

内なのでforeachの中に居る状態なので、seatsの中にある変数については meal のような形で使えるけど、それよりも上の階層の変数を参照する場合、ViewModelのルートの変数なら$root.hoge、一個上の階層の変数なら$parents[0].hoge($parent.hogeでも可)、2個上の階層なら$parents[1].hoge…というように書いていく必要があるらしい。

ふむ、なるほど。