Is it possible to pass to Knockout the default value of a select element when binding the options through AJAX?

StackOverflow https://stackoverflow.com//questions/9570173

Domanda

I need to fetch both the current value and the available options of a <select /> through two different AJAX calls (I can't queue/chain them for a couple of reasons).

I don't know if it matters but the call reading the current value completes before the call reading the available options every time.

Using Knockout 2.0.0 and Knockout Mapping 2.0.3 I bound the select with data-bind="options: values, value: valueComputed", but I found out that Knockout is removing/ignoring the value set by the first AJAX call, until the call fetching the available options completes is not possible to set the current value.

Is this correct? Is it possible to tell Knockout "this is the current value, when the available options become available select it"?

After a couple of tests I came up with a kludge: instead of binding a plain observable to the value of the select I used a computed observable where I intercept the value and do not alter the underlying observable if the new value is undefined.

Am I doing something evil?

A jsFiddle example: http://jsfiddle.net/KeNUU/

The JavaScript code I'm using:

var viewModel = function () {
    var self = this;

    // The underlying observable where
    // the selected value is stored.
    self.value = ko.observable();

    // The observable bound to
    // the value of the drop down.
    self.values = ko.mapping.fromJS([]);

    // Use a computed observable to intercept the undefined
    // value placed by KnockOut when binding the drop down.
    self.valueComputed = ko.computed({
        "read": function () {
            return ko.utils.unwrapObservable(self.value);
        },
        "write": function (value) {
            // Update the underlying observable only when
            // there is a value, if it's undefined ignore it.
            if (value) {
                self.value(value);
            }
        }
    });

    // Simulate the AJAX request which fetches the actual value,
    // this request must complete before the second one.
    setTimeout(function () {
        self.valueComputed("b");
    }, 1000 * 1);

    // Simulate the AJAX request which fetches the available values,
    // this reqest must complete after the first one.
    setTimeout(function () {
        ko.mapping.fromJS(["a", "b", "c", "d"], {}, self.values);
    }, 1000 * 2);
};

$(document).ready(function () {
    ko.applyBindings(new viewModel());
});
È stato utile?

Soluzione

I don't think that what you are doing is going to cause you a problem, but you are circumventing KO's logic to ensure that what is bound to value is valid choice in your options. Additionally, if you were using optionsCaption to provide a blank choice, then you would not be able to clear out your value. In your case, it likely would not matter.

I think that an easier choice is to just cache the initial value in a non-observable and then use it to populate value when the list comes back. If the list came first, then there would be no initial value and it could work as normal.

Something like:

setTimeout(function () {
    self.initialValue = "b";
    self.value("b");
}, 1000 * 1);

// Simulate the AJAX request which fetches the available values,
// this reqest must complete after the first one.
setTimeout(function () {
    ko.mapping.fromJS(["a", "b", "c", "d"], {}, self.values);
    if (self.initialValue) {
         self.value(self.initialValue);
         self.initialValue = null;
    }    
}, 1000 * 2);

http://jsfiddle.net/rniemeyer/gB3E5/

Now, you handle this upfront without the (minor) overhead of having a computed observable intercept every change to the dropdown.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top