Question

I have the following scenario.

Exemplary JavaScript:

function Foo() {
   this.id = ko.observable("KEY_1"); //Current selected item
   this.list = [{ id: "KEY_2", text: "Two" }, { id: "KEY_3", text: "Three" }]; //All available items
}

ko.applyBindings(new Foo());

This is bound to an HTML-select with the Help of the value and options bindingHandlers.

data-bind="value: id, optionsEx: list"

May the current selected item isn't included in the list of all available items because it was deleted on the server and should not longer beeing able to get selected. Some entities still have the value of KEY_1 set to id for historical reasons. What I want is that if the value of id isn't in the list any more, it should be added to the list as dummy entry called 'Deleted' by the bindingHandler dynamically on the client.

I tried the following

ko.bindingHandlers["optionsEx"] = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var allBindings = allBindingsAccessor(),
            optionsValue = allBindings['optionsValue'],
            value = ko.utils.unwrapObservable(allBindings['value']),
            list;

        if (value) {
            list = //external function searching the list for the item any adding it if missing
            ko.bindingHandlers.options.update(element, function () { return list; }, allBindingsAccessor);
        } else {
            ko.bindingHandlers.options.update(element, valueAccessor, allBindingsAccessor);
        }
    }
};

But this doesn't work. Can anyone give me a hint to get this achieved?

Update: I have created an jsfiddle. Its curiouse because the code is working in the jsfiddle but not at our development branch. I have to examine why. But maybe someone has a better idea to achieve the functionality.

http://jsfiddle.net/philipooo/C46A8/

Update: As I see the order of the bindingHandlers is mission critical. Maybe this solves my problem.

Was it helpful?

Solution

I had a look at the various ways of doing this and came up with adding the new value to the base list specified in the options if it doesn't exist. Go straight to a fiddle

businessUnitsList: ko.observableArray([{
    id: "a",
    title: "business1"
}, {
    id: "b",
    title: "business2"
}, {
    id: "c",
    title: "business3"
}, {
    id: "d",
    title: "business4"
}]),

The base list array must be an observableArray so it gets updated automatically by knockout when a new value is added), this then triggers the list to refresh.

The HTML to set up the binding:

<select data-bind="missingText:'{val} is DELETED',
   options:businessUnits.businessUnitsList,
   optionsText:'title',
   optionsValue:'id', 
   value:businessUnits.currentlySelected">
</select>

The "missingText" property hooks into the binding handler and allows the text to be configured, along with the value that was not found as a token in the text {val}

And inside the binding handler which I have called "missingText"

    ko.bindingHandlers["missingText"] = {

    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bind) {
    var allBindings = allBindingsAccessor(),
        items = allBindings['options'],
        value = ko.utils.unwrapObservable(allBindings['value']),
        valueProperty = ko.utils.unwrapObservable(allBindings['optionsValue']),
        textProperty = ko.utils.unwrapObservable(allBindings['optionsText']),
        missingTextProperty= ko.utils.unwrapObservable(allBindings['missingText']),
        valueSetter = allBindings['value'],
        list
    //we must have the two properties specified
    if (!valueProperty || !textProperty){
        throw ("missingText requires the optionsText and optionsValue property to be provided");   
    }

    if (value) {
        //try and find the currentlySelected value in the list
        var found = ko.utils.arrayFilter(items(), function (item) {
            //we're binding to a particular field for the value
            //so look for that as the value
            return item[valueProperty] == value;
        });

        //if we haven't found it in the list, add it with our missingText text
        if (found.length === 0) {

            var newItem ={};
            newItem[valueProperty]=value;
            //replace token with the value that's missing
            newItem[textProperty]=missingTextProperty.replace('{val}', value);  
        //adding the new item to the items list will trigger the list to refresh
            //and display our new value                
        items.push(newItem);

        }

and here is the fiddle again

Hopefully that helps?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top