Knockout custom binding handlers: $root is undefin

2019-07-17 02:43发布

I am using a Knockout Custom Binding handler (borrowed from Creating groups with Knockout.js foreach). Within the nested markup, I'd like to reference an observable that is located on the root of the view model. However the binding fails because $root is undefined. The same markup works fine with a standard foreach binding. I don't know why the custom hander prevents using $root.

Here is the source for the binding handler :

ko.bindingHandlers.foreachGrouped = {
init: function(element, valueAccessor) {
     var groupedItems,
         options = valueAccessor();

    //create our own computed that transforms the flat array into rows/columns
    groupedItems = ko.computed({
        read: function() {
            var index, length, group,
                result = [],
                count = +ko.utils.unwrapObservable(options.count) || 1,
                items = ko.utils.unwrapObservable(options.data);

            //create an array of arrays (rows/columns)
            for (index = 0, length = items.length; index < length; index++) {
                if (index % count === 0) {
                   group = [];
                   result.push(group);
                }

                group.push(items[index]);
            }

            return result;
        },
        disposeWhenNodeIsRemoved: element
    });  

    //use the normal foreach binding with our new computed
    ko.applyBindingsToNode(element, { foreach: groupedItems });

    //make sure that the children of this element are not bound
    return { controlsDescendantBindings: true };
}
};

Here is the html markup:

Header Text: <input data-bind="value: header" />
Group count: <input data-bind="value: count" />

<div data-bind="foreachGrouped: { data: items, count: count }">
   <h1 data-bind="html: $root.header"></h1>

   <ul data-bind="foreach: $data">
       <li data-bind="text: $data"></li>
   </ul>
</div>

And here is the code used to wire up the view model:

ko.applyBindings({
  header: ko.observable("Group Header"),
      items: ko.observableArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
      count: ko.observable(4)
});

Example: http://jsfiddle.net/dk1do2vr/2/

1条回答
在下西门庆
2楼-- · 2019-07-17 03:25

The problem is that the original bindingContext is lost in the handler. So when ko.applyBindingsToNode() is called, it uses a completely new context (which is empty). The handler predated when being able to specify what the binding context is, it was added in a later version of knockout. You'll need to make adjustments to the handler to be able to preserve that context.

ko.applyBindingsToNode(element, { foreach: groupedItems }, bindingContext);

So the tweaks you need to make in the handler (removed irrelevant bits to be easier to see):

ko.bindingHandlers.foreachGrouped = {
    // need to get the binding context (fifth param)
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        //...

        //use the normal foreach binding with our new computed
        ko.applyBindingsToNode(element, { foreach: groupedItems }, bindingContext); // pass in the binding context

        //...
    }
};

updated fiddle

查看更多
登录 后发表回答