3

I have a knockout model which has multiple observable properties:

var personViewModel = {
    name: ko.observable('Bob'),
    age: ko.observable(123)
};

I would like to create a custom binding which renders person view model. However, I would like this binding to update if any of the child properties, i.e. name or age, are updated.

With a bindingHandler the update method is only fired when the bound observable property is updated, not when a child property on the bound observables change.

As a workaround I am adding subscriptions to the child properties in the init function:

ko.bindingHandlers.foo = {
    init: function (element, valueAccessor, allBindingsAccessor,
                    viewModel, bindingContext) {
        // setup code goes here ... DOM elements inserted etc....

        valueAccessor().age.subscribe(function () {
            // Update the UI
        });
        valueAccessor().name.subscribe(function () {
            // Update the UI
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor,
                      viewModel, bindingContext) {
        // Update the UI
    }
};

NOTE: this is a simplified example, I do have a generic approach for subscribing to multiple child observables!

Is this a good way to approach the problem? Or is there some built-in Knockout feature that I am overlooking here?

ColinE
  • 68,894
  • 15
  • 164
  • 232

2 Answers2

4

ko.toJS will hookup the dependencies, so if you call this in your update it will resolve all dependencies for you

http://jsfiddle.net/rMG8y/

ko.bindingHandlers.foo = {
    update: function(element, valueAccessor) {
        //Resolve dependency
        var dependency = ko.toJS(valueAccessor());

        //Do whatever
        console.log(dependency);
    }
};
Anders
  • 17,306
  • 10
  • 76
  • 144
  • 1
    awesome - that works (+1), but ... how does it work? I understand what `toJS` does, but not why this should cause the binding to update when any of the values change? – ColinE Sep 17 '13 at 20:26
  • 1
    Well, it iterates the object tree and unwraps the observables, that will resolve the dependencies – Anders Sep 17 '13 at 20:56
0

In addition to what has been previously mentioned, I found the following article to be very helpful in understanding how knockout binding handlers work with dependencies:

http://www.knockmeout.net/2012/06/knockoutjs-performance-gotcha-3-all-bindings.html

In short, all the binding handlers for a given element run in the context of a single computed observable (there is a note in the article that this might change so that each binding handler runs in the context of it's own computed observable - this happened in 3.0). Knockout dependencies are recreated each time the function in a computed observable is evaluated, which is why you have to unwrap the observable in the update every time to not lose the dependency as you would if you only unwrap the observable in the init.

In the article, he presents an option to create a computed observable in the init of your binding handler to tie whatever observables you want to some function:

        var args = arguments;
        ko.computed(function ()
        {
            ko.utils.unwrapObservable(valueAccessor()/*or whatever observable you want to tie the update to*/);
            doUpdateLogic(/*args or whatever you need*/);
        }, this);

This obviously is dangerous, so use with care :)

Community
  • 1
  • 1
mheskamp
  • 74
  • 6