Jason
Jason

Reputation: 8640

In Knockout, how can I delay databinding until an array is populated?

I've run into this pattern several times now and haven't found an elegant way to handle it. I'm trying to set up a page with a read-only and edit mode.

Here is the html:

<div>
    <span data-bind="text: EventType, if: !EditMode()"></span>
    <select data-bind="options: $root.EventTypes, optionsValue: 'Id', 
        optionsText: 'Name', value: EventType, if: EditMode()"></select>
</div>
<div>
    <a href="#" data-bind="click: edit">Edit</a>
</div>

And the javascript:

function viewModel(eventJSON) {
    var self = this;

    self.EventType = ko.observable(eventJSON.EventType);
    self.EventTypes = ko.observableArray([]);

    self.edit = function() {
        $.getJSON("url...", function(eventTypesJSON) {
            self.EventTypes(eventTypesJSON);
        }
    }
}

I'm running into a timing issue with the databinding. When applyBindings is called, the span gets populated correctly. However, when I enter edit mode, the drop down list is not set to the selected value and EventType is set to undefined.

Here's what I think is happening:

  1. The user clicks the Edit button and an ajax call is made to retrieve the EventTypes.
  2. The select is databound. Since the EventTypes array is empty, EventType is set to null.
  3. The ajax call completes and EventTypes is populated.
  4. Since EventType was previously set to null, the first item in the drop down is selected. (e.g. the original EvenType value is lost.)

What I'd like it to do:

  1. The user clicks the Edit button and an ajax call is made to retrieve the EventTypes.
  2. The ajax call completes and the EventTypes array is populated.
  3. The select is databound and the correct item in the list is selected.

Upvotes: 0

Views: 2242

Answers (2)

Otis
Otis

Reputation: 1072

I believe I had the same problem today and found a solution for it:

  • The SELECT gets the value and text for the options from an observable array.
  • The observable array's data is loaded after the rest of the view model has been filled.
  • The selected value for the array comes from another field of the view model.

I was able to make it work by passing the selected value in to the function that loads the array from a web service call. After the call has been completed, I assign the value back to the observable originally used as the selected value. Doing so forces the subscribers to that field to re-bind so the SELECT that is now filled correctly changes the selected value.

I know this is late for you and may not be exactly your situation but it sounds similar and might help someone else.

Upvotes: 2

Grim
Grim

Reputation: 1638

Instead of setting up the EventTypes array as empty, bootstrap it with the event type that's initially selected, so it won't clear the EventType value when the select is enabled. The content will be overwritten anyway once the ajax returns from the call. i.e.:

self.EventTypes = ko.observableArray([eventJSON.EventType]);

Alternatively, if for some reason you can't initialize the array beforehand, and since the initial event is still in scope of the ajax callback, simply reset EventType just after loading the array:

$.getJSON("url...", function(eventTypesJSON) {
   self.EventTypes(eventTypesJSON);
   self.EventType(eventJson.EventType);
}

In the latter, just be careful not to use the same name for the initial view model parameter and the one in the ajax callback, or the latter will overwrite the first.

Upvotes: 1

Related Questions