1
votes

I'm having a problem with a custom knockout binding handler that creates a context. The demonstration is here: http://jsfiddle.net/gf3tfarz/14/

When 'container' is applied to an observable array, it's not updating the children elements.

<div data-bind="container: { data: selectedCountry().ids }">
    <p>Error: <span data-bind="text: $container.data().length"></span></p>
    <div data-bind="foreach: $container.data">
        <p>Error: <span data-bind="text: $data"></span></p>
    </div>
</div>

This is the custom binding handler:

ko.bindingHandlers.container = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var innerBindingContext = bindingContext.extend({
            $container: ko.utils.unwrapObservable(valueAccessor())
        });

        ko.applyBindingsToDescendants(innerBindingContext, element);

        return { controlsDescendantBindings: true };
    }
};

I'd like a way that both examples using 'container' present in the demonstration work.

Note that using "with" it works.

1
Sorry, what is wrong with the fiddle? What are we looking at? How do we know it is not working? You know, what is expected? What is actually happening? It seems functional to me. - Jeff Mercado
All the texts after "Error:" does not update when the select is changed. That's the error. In my opinion, it should change like all the texts after "Ok:", which uses different techniques. - Adriano dos Santos Fernandes
One more or less easy solution would be to turn your $container into a computed: jsfiddle.net/wn5zp9qu. However in this case you have to write $container() every where you want to use it: data-bind="foreach: $container().data" - nemesv
That's good @nemesv. I was looking into something similar, but was unable to construct it myself. Just now need to see if it works in my real code, which is much more complex than this test. - Adriano dos Santos Fernandes
you can also use ko.contextFor(element) - segFault

1 Answers

0
votes

You shouldnt be extending the bindingContext directly, you should create a child binding context. Then it will work how you expect it to.

ko.bindingHandlers.container = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {       
        // Make a modified binding context, with a extra properties, and apply it to descendant elements
        var innerBindingContext = bindingContext.createChildContext(
            bindingContext.$rawData, 
            null, // Optionally, pass a string here as an alias for the data item in descendant contexts
            function(context) {
                ko.utils.extend(context, {
                    $container: ko.utils.unwrapObservable(valueAccessor())
                });
            });

        ko.applyBindingsToDescendants(innerBindingContext, element);

        return { controlsDescendantBindings: true };
    }
};

See the bottom of this page for more info... http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html