user1265146
user1265146

Reputation: 2065

How do I implement multiple select lists ensuring only unique values are available to all?

I need to ensure uniqueness within my select lists.

I have a list of available options:

var viewModel = 
{
    availableOptions : ['Bicycle','Car','Shuttle','Motorcycle','Motorcycle'],
    items : [{id:1, selectedOption: ko.observable('Car')}, 
             {id:2, selectedOption: ko.observable()}, 
             {id:3, selectedOption: ko.observable()}]
}

I want to foreach through my items but ensure no two items can have the same option:

<-- ko foreach: items -->
  <select data-bind="options: $parent.availableOptions, value: selectedOption, optionsCaption: 'Choose...'"></select>
<!-- /ko -->

Any ideas on how to successfully achieve this where the default values stick, and only the remaining options are available to my other select's ?

Upvotes: 0

Views: 163

Answers (3)

Max Brodin
Max Brodin

Reputation: 3938

You need computed here to filter items for each select

this.availableOptions = ko.computed(function () {
    var item, option, i, j, isAvailable, result = [];
    for (i = 0; i < root.availableOptions.length; i++) {            
        option = root.availableOptions[i];
        isAvailable = true;
        for (j = 0; j < root.items.length; j++) {
            item = root.items[j];
            if (item.id !== this.id && item.selectedOption() === option) {
                isAvailable = false;
            }
        }
        if (isAvailable) {
            result.push(option);
        }
    }
    return result;
}, this, { deferEvaluation: true });   

See working fiddle

Edited Added deferEvaluation to make all items to depend on all items

Upvotes: 2

Tomalak
Tomalak

Reputation: 338128

The cleanest way of course is not sending uselessly duplicated data to the browser in the first place.

The next best solution is to use an array-unique function. Most JS libraries offer such a function. For no particular reason I'm using lodash's uniq() but you can use any library you like, for example jQuery's $.unique().

var viewModel = {
    availableOptions : _.uniq(['Bicycle','Car','Shuttle','Motorcycle','Motorcycle']),
    items : [{id:1, selectedOption: ko.observable('Car')}, 
             {id:2, selectedOption: ko.observable()}, 
             {id:3, selectedOption: ko.observable()}]
};

In general I would recommend using a class for your view model so you can do some sort of processing while you construct it. Using object literals for view models only works for the simplest cases.

For example like this.

function ViewModel(data) {
    var self = this, i;

    self.availableOptions: ko.observableArray(_.uniq(data.availableOptions));
    self.items = ko.observableArray();

    for(i = 1; i <= data.itemCount; i++) {
        self.items.push({id:i, selectedOption: ko.observable()});
    }
}

var viewModel = new ViewModel({
    availableOptions: ['Bicycle','Car','Shuttle','Motorcycle','Motorcycle'],
    itemCount: 3
});
ko.applyBindings(viewModel);

Upvotes: 0

gkb
gkb

Reputation: 1459

Try something like below -

selectedOption.subscribe(function (newValue) {
    var index = availableOptions.indexOf(selectedOption());
    if (index > -1) {
        availableOptions.splice(index, 1);
    }
});

Upvotes: 0

Related Questions