rubens.lopes
rubens.lopes

Reputation: 2535

binding options: in a select fails to pick the correct value on init

I have the following select:

<select data-bind=" options: states, 
                    optionsText: 'acronym', 
                    optionValue: 'stateId', 
                    value: state">
</select>

ViewModel and dependencies:

    function State(state) {
        var self = this;
        self.stateId = ko.observable(state.StateId);
        self.acronym = ko.observable(state.Acronym);
    }

    function AddressViewModel() {
        var self = this;

        self.cities = ko.observableArray([]);
        self.states = ko.observableArray([]);
        self.zipcode = ko.observable();
        self.city = ko.observable();
        self.state = ko.observable();
        self.stateHard = ko.observable();
        self.address = ko.observable();
        self.addressNumber = ko.observable();
        self.address2 = ko.observable();
        self.town = ko.observable();
        self.phoneNumber = ko.observable();
        self.mobileNumber = ko.observable();

        self.state.subscribe(function () {
            self.UpdateCities();
        });

        self.UpdateCities = function () {
            if (self.state())
                $.getJSON("/api/v1/address/cities/" + self.state().stateId(), 
                          function (data) {
                    self.cities($.map(data, function (item) { 
                                                    return new City(item) }));
                });
        }

   $.getJSON("/api/v1/user/address/", function (data) {
                    self.zipcode(data.zipcode);
                    self.city(new City(data.city));
                    self.state(new State(data.state));
                    self.address(data.address);
                    self.addressNumber(data.addressNumber);
                    self.address2(data.address2);
                    self.town(data.town);
                    self.phoneNumber(data.phoneNumber);
                    self.mobileNumber(data.mobileNumber);

            $.getJSON("/api/v1/address/states", function (data) {
                self.states($.map(data, function (item) { 
                                                 return new State(item) }));
            });
        });
    }

But when I involke ko.applyBindings(new AddressViewModel()); the AVM.state loses its original value, although that value is in the list.

I already tried to invert the getJSON order (states before state and vice versa) but nothing new happened.

Upvotes: 0

Views: 137

Answers (2)

Paul
Paul

Reputation: 1502

There's actually a few issues going on here.

  1. You spelled the optionsValue value wrong in your data-bind section.
  2. If you spelled the optionsValue option correctly, when you choose an item from the drop-down, you will be writing State.stateId to AVM.state; however in your initialisation call you create an object: self.state(new State(data.state)). You can't expect that to work; you're initialising with an object reference but updating with a literal value.
  3. If you spelled the optionsValue option incorrectly and would like to keep it working this way - first up delete optionValue so you don't get confused. Next - be aware that an object will be written to AVM.state. The object equivalence testing is done by reference, not by deep-inspection of the objects values. You would need to pull a reference from AVM.states and write it to AVM.state. Something like this:

    // Use the data.state.StateId to find the correct state from the states list.
    self.state(ko.utils.arrayFirst(self.states(), function (s) {
        return s.stateId() === data.state.StateId;
    }));
    

The order of initialisation is important IF the databinding occurs before you've finished all your AJAX calls. If databinding occurs before all ajax calls are complete the following is the sequence of events:

  1. AJAX calls for state/states started.
  2. ko.applyBindings(AVM); is called, options: binds against the empty AVM.states list.
  3. AJAX for /api/v1/user/address completes, AVM.state is set.
  4. options: binding notes that AVM.state is not in AVM.states and writes NULL to AVM.state.
  5. AJAX for /api/v1/address/states completes, AVM.states is set.
  6. options: binding now uses AVM.states to update the dropdown with options, and by default the first one is selected.
  7. You look at the UI wondering "what happened?"

I've attached references to a couple JSFiddles showing two approaches to the problem.

Version 1 - Using optionsValue in the data-bind call: http://jsfiddle.net/Lam42/1/

Version 2 - Removing optionsValue and using object references: http://jsfiddle.net/sFW4j/

Upvotes: 2

David W Lee
David W Lee

Reputation: 169

You have to create a function and move the code block $.getJSON("/api/v1/user/address/",.. inside the function. And then, call that function after invoking ko.applyBindings(new AddressViewModel());.

Upvotes: 0

Related Questions