Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout: how to update another binding from custom binding?

We have usual problem with optionsCaption binding: it is always showed even when there is only one element. We solved this problem using our custom binding:

ko.bindingHandlers.optionsAutoSelect = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var allBindings = allBindingsAccessor();

        if (value.length == 1) {
            allBindings.optionsCaption = null;
        }
        ko.bindingHandlers.options.update(element, valueAccessor, allBindingsAccessor);
    }
};

after updating to knockout 3.0 allBindings become readonly. So any changes are really skipped. Any ideas how it could be solved in ko 3.0? We really have a lot of such auto selects and don't want to copy paste some computed code on all views. So we want some single option/extensibility point. Unfortunately as I could see options binding is rather monolithic.

like image 651
Yauhen.F Avatar asked Dec 06 '13 14:12

Yauhen.F


1 Answers

Probably some different approaches that you could take here. Here is one thought (feel free to use a better name than optionsPlus):

ko.bindingHandlers.optionsPlus = {
    preprocess: function(value, name, addBindingCallback) {
        //add optionsCaption to the bindings against a "caption" sub-observable
        addBindingCallback("optionsCaption", value + ".caption"); 

        //just return the original value to allow this binding to remain as-is
        return value;
    },
    init: function(element, valueAccessor) {
        var options = valueAccessor();

        //create an observable to track the caption
        if (options && !ko.isObservable(options.caption)) {
            options.caption = ko.observable();
        }

        //call the real options binding, return to control descendant bindings
        return ko.bindingHandlers.options.init.apply(this, arguments);
    },
    update: function(element, valueAccessor, allBindings) {
        var options = valueAccessor(),
            value = ko.unwrap(options);

        //set the caption observable based on the length of data
        options.caption(value.length === 1 ? null : allBindings.get("defaultCaption"));

        //call the original options update
        ko.bindingHandlers.options.update.apply(this, arguments);        
    }

};

You would use it like:

<select data-bind="optionsPlus: choices, defaultCaption: 'choose one...'"></select>

It creates a caption observable off of your observableArray/array and updates the caption initially and whenever the options are updated (if using an observableArray).

Sample here: http://jsfiddle.net/rniemeyer/jZ2FC/

like image 82
RP Niemeyer Avatar answered Sep 21 '22 18:09

RP Niemeyer