SuneRadich
SuneRadich

Reputation: 196

Custom binding for a select can not handle objects as a value

I have a setup, where I populate a select with options based on a observableArray. This array contains objects with more properties than just an id and a name. I want to be able to use the full javascript object as the selected value.

The data is like this:

this.optionData = ko.observableArray([
    {id: 1, name: "One", param: true},
    {id: 2, name: "Two", param: true},
    {id: 3, name: "Three", param: true},
]);

And with standard bindings in knockout its nice and easy

<select data-bind="
   options: optionData,
   optionsText: 'name',
   optionsCaption: 'Please select',
   value: selectedOption">
</select>

However, I want to style my selects, and add more functionality to them. I use select2 for this. I've come up with the following custom binding, and this works fine for simple bindings with both a optionsText and optionsValue - but it does not work for options bindings without the optionsValue (ie. when using objects as a value).

ko.bindingHandlers.select2 = {
init: function (el, valueAccessor, allBindingsAccessor, viewModel) {

    ko.utils.domNodeDisposal.addDisposeCallback(el, function () {
        $(el).select2('destroy');
    });

    var allBindings = allBindingsAccessor(),
        select2 = ko.utils.unwrapObservable(valueAccessor());

    $(el).select2(select2);
},

update: function (element, valueAccessor, allBindingsAccessor, viewModel) {

    var selectedOptions = ko.unwrap(allBindingsAccessor.get("selectedOptions")),
        val = ko.unwrap(allBindingsAccessor.get("value")),
        options = ko.unwrap(allBindingsAccessor.get("options"));

    if ($(element).prop('multiple')) {
        $(element).select2('val', selectedOptions, true);
    }
    else {
        $(element).select2("val", val);
    }

    $(element).trigger('change');
}

};

My question is this, what do I need to update in order for my custom binding to work as the default knockout options binding?

I've made this fiddle to demonstrate the issue: http://jsfiddle.net/SuneRadich/LdF45/1/

Upvotes: 1

Views: 971

Answers (2)

Tarken
Tarken

Reputation: 2202

My example might help with using optionsAfterRender to get the objects.

<select id="selectArticles" multiple
 data-bind="options: availableArticles, selectedOptions: selectedArticles,
 optionsText: function(item){return item.title},
 optionsAfterRender: function(option, item){option.value = item}">
</select>

workding fiddle

If necessary I can change it to single select.

Upvotes: 1

Muhammad Raheel
Muhammad Raheel

Reputation: 19882

You can use optionAfterRender property for adding more functionality to your select.

Consider this example

<select 
    data-bind="
        options: options,
        value : selectedOption,
        optionsText: 'Name',
        optionsAfterRender: $root.setTitle
    "
 ></select><br />
<button data-bind="click: showSelectedOptions">Show selection</button>

function Option(id, name){
    var self = this;

    self.Id = id;
    self.Name = name;
}

function ViewModel(){
    var self = this;

    self.options = ko.observableArray([
        new Option(0, "NormalText"),
        new Option(1, "AnotherText"),
        new Option(2, "WaaaaaaaaaaaaaaaayTooLongText")
    ]);
    self.selectedOption = ko.observable();

    self.showSelectedOptions = function(){
        console.log(self.selectedOption());
    }

    self.setTitle = function(option, item) {
        option.title = item.Name
        // other properties
    }     
}

ko.applyBindings(new ViewModel());

SQL Fiddle Demo

Make sure you have version 3.1.0

Upvotes: 1

Related Questions