The normal behavior in Knockout is for all bindings on an element to be linked together. Their dependencies are combined so that an update to any of the observables in the bindings will update all the bindings.
This can be a performance issue because updating some bindings is relatively expensive (options, template). Take following example from an application I'm developing:
The current page will have isCurrent == true, while all others will have isCurrent == false. With the above code, whenever the current page changes, the template is also re-rendered. My initial solution was to wrap the div in another and move the 'visible' binding to the outer div:
The async wrapper binding can be used to isolate bindings that modify a property of an element and are likely to be updated dynamically, such as visible, enable, and hasfocus, from bindings that are used to generate content, such as template, html, and options. See http://groups.google.com/group/knockoutjs/browse_thread/thread/d58dc5... about using this with 'options'.
Here is the source for the binding:
ko.bindingHandlers['async'] = { 'init': function(node, valueAccessor, parsedBindingsAccessor, viewModel, bindingContextInstance) { var parsedBindings = valueAccessor(); function makeValueAccessor(bindingKey) { return function () { return parsedBindings[bindingKey] } } var binding, bindingKey; for (bindingKey in parsedBindings) { if (!(binding = ko.bindingHandlers[bindingKey])) { continue; } if (!ko.isObservable(parsedBindings[bindingKey])) { throw new Error('async binding must be used with observables only'); } ko.dependentObservable((function(bindingKey, binding){ var isInit = false; return function () { if (!isInit && typeof binding["init"] == "function") { var initResult = binding["init"](node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance); isInit = true; } if (typeof binding["update"] == "function") { binding["update"](node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance); } } })(bindingKey, binding), null, {'disposeWhenNodeIsRemoved': node}); } }
I think it might be important to note that if your binding definition dereferences the observable directly [1] then the async binding will not work as expected. Not a deal breaker, but a caveat worth mentioning.
The text binding will be evaluated whenever observableB is updated because as it is processed currently, the observableB will be a dependency of the whole DO, not the async DO.
On Thu, Dec 1, 2011 at 12:14 PM, Michael Best <mb...@dasya.com> wrote: > The normal behavior in Knockout is for all bindings on an element to > be linked together. Their dependencies are combined so that an update > to any of the observables in the bindings will update all the > bindings.
> This can be a performance issue because updating some bindings is > relatively expensive (options, template). Take following example from > an application I'm developing:
> The current page will have isCurrent == true, while all others will > have isCurrent == false. With the above code, whenever the current > page changes, the template is also re-rendered. My initial solution > was to wrap the div in another and move the 'visible' binding to the > outer div:
> The async wrapper binding can be used to isolate bindings that modify > a property of an element and are likely to be updated dynamically, > such as visible, enable, and hasfocus, from bindings that are used to > generate content, such as template, html, and options. See > http://groups.google.com/group/knockoutjs/browse_thread/thread/d58dc5... > about using this with 'options'.
> Here is the source for the binding:
> ko.bindingHandlers['async'] = { > 'init': function(node, valueAccessor, parsedBindingsAccessor, > viewModel, bindingContextInstance) { > var parsedBindings = valueAccessor(); > function makeValueAccessor(bindingKey) { > return function () { return parsedBindings[bindingKey] } > } > var binding, bindingKey; > for (bindingKey in parsedBindings) { > if (!(binding = ko.bindingHandlers[bindingKey])) { > continue; > } > if (!ko.isObservable(parsedBindings[bindingKey])) { > throw new Error('async binding must be used with > observables only'); > } > ko.dependentObservable((function(bindingKey, binding){ > var isInit = false; > return function () { > if (!isInit && typeof binding["init"] == > "function") { > var initResult = binding["init"](node, > makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, > bindingContextInstance); > isInit = true; > } > if (typeof binding["update"] == "function") { > binding["update"](node, > makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, > bindingContextInstance); > } > } > })(bindingKey, binding), null, > {'disposeWhenNodeIsRemoved': node}); > } > } > };
> I think it might be important to note that if your binding definition > dereferences the observable directly [1] then the async binding will > not work as expected. Not a deal breaker, but a caveat worth > mentioning.
After the 1.3 release it would be worth investigating the possibility of making the binding's as lazy as possible.
I know each binding handler takes a function that will return the value of it's key-value pair. However this function is created much too late in the process, as such bindings get "evaluated" and then the values get wrapped in functions (or not if they are observables, or something). It would be really great if we could combine the two different bits of code: