How to display text of selectedOptions from multiselect list of array of objects in knockout.js

883 Views Asked by At

I have a model like this

function SearchFilterOption(data){
    var self    =   this
    self.List           =   ko.observableArray([])
    self.SelectedOptions    =   ko.observable(0)
    self.SelectedOptionText     =   ko.computed(function(){
        return self.SelectedOptions()
    })
}

And here is binding

<select multiple="multiple"
    data-bind="
        options:List,
        optionsText:"Description",
        optionsValue:"Value",
        optionsCaption:"---",
        value:0,
        selectedOptions:SelectedOptions"
></select>  

And here is json of list.

"Countries": [
            {"Value": 1,"Description": "United States"}, 
            {"Value": 2,"Description": "Canada"},
            {"Value": 3,"Description": "United Kingdom"},
            {"Value": 4,"Description": "Pakistan"},
            {"Value": 5,"Description": "India"}
]

Now when i select multiple countries their ids are stored in selectedOptions and displayed in this binding

<p data-bind="text:SelectedOptionText"></p> 

this all works well but i want to display comma seperated text instead of ids like United States , Canada etc. How can i do that. I have tried this

self.SelectedOptionText     =   ko.computed(function(){
    var selectedItems = ko.utils.arrayFilter(self.List(),function(item){
        if(in_array(item.Value,self.SelectedOptions())){
            return item.Description
        }
    })

    return selectedItems.join(',')          
})  

But it always returns object instead of Description.Also if the array is large it slow down on browser.
Note : in_array is a utility taken from here phpjs.org.

1

There are 1 best solutions below

0
nemesv On BEST ANSWER

ko.utils.arrayFilter as the name implies only filters your items it does not transform them.

You need a separate ko.utils.arrayMap where you get the Description of each item what you can now join together:

self.SelectedOptionText = ko.computed(function () {
    var selectedItems = ko.utils.arrayFilter(self.List(), function (item) {
        return in_array(item.Value, self.SelectedOptions());
    });
    var selectedDescriptions = ko.utils.arrayMap(selectedItems, function (item) {
        return item.Description;
    });
    return selectedDescriptions.join(',');
})

Note: you have also misused the in_array in your ko.utils.arrayFilter because in the callback you need to return true for an element if you want to include so you can just return the result of the in_array call.

Demo JFiddle.

Or you can do the same lookup and mapping in one ko.utils.arrayForEach call:

self.SelectedOptionText = ko.computed(function () {
    var selectedDescriptions = [];
    ko.utils.arrayForEach(self.List(), function (item) {
        if (in_array(item.Value, self.SelectedOptions()))
            selectedDescriptions.push(item.Description);
    });
    return selectedDescriptions.join(',');
})

Demo JSFiddle.