0

I have seen some solutions elsewhere for this problem, but have not yet found how to make this work in my specific circumstances.

An observableArray populates an html list. Inside the list, a user can edit one of the values for a particular row. The item to be edited in the row is itself an observableArray that populates a dropdown list. The array for the drop down list is populated via an ajax call when an edit button on the row is clicked (edit button not shown in code below).

I have this all working fine, except for one important issue: as of yet, the value in the dropdown list is not being pre-populated.

Below is a gross mockup of what the real code is doing.

If I call selectedTeamType as selectedTeamType() in the html, then the intial value is populated, but further changes to the select box are not registered. If I call selectedTeamType as just selectedTeamType, then the intial value is not set properly, but further changes to the select box are registered.

HTML

<table>
    <tr>    <!-- ko foreach: my.TeamViewModel.Teams -->
      <td data-bind="text: TeamID"></td>
      <td data-bind="text: TeamText"></td>
      <td>  <!-- the TeamType will be editable, so we provide a dropdown list, but default to the current value -->
         <select data-bind="options: my.TeamViewModel.TeamTypes, 
             optionsText: 'TeamTypeText', 
             optionsValue: 'TeamTypeID', 
                 value: selectedTeamType"></select>
        </td>
   </tr> <!-- /ko -->
</table>

JAVASCRIPT

var my = my || {};

$(function () {

my.TeamModel = function () {
    var self = this;
    self.TeamID = ko.observable();
         self.TeamText = ko.observable();
    self.TeamTypeID = ko.observable();
    self.selectedTeam = ko.observable(); 
         self.Edit = function () {
            my.TeamViewModel.load_TeamTypes();
        };
};

my.TeamTypesModel = function () {
    var self = this;
    self.TeamTypeID = ko.observable();
    self.TeamTypeText = ko.observable();
};

my.TeamViewModel = function () {
    var Teams = ko.observableArray([]),
        TeamTypes = ko.observableArray([]),
        load_TeamTypes = function () {
            $.ajax({
                      // only part of ajax call displayed here for sake of brevity...              
                                $.each(data, function (i, d) {
                                    TeamTypes.push(new my.TeamTypesModel()
                                            .TeamTypeID(d.TeamTypeID)
                                            .TeamTypeText(d.TeamTypeText)
                                    );
                                });
            });
        },
        load_Teams = function () {
            $.ajax({
                      // only part of ajax call displayed here for sake of brevity...   
                    $.each(data, function (i, d) {
                        Teams.push(new my.TeamModel()
                                .TeamID(d.TeamID)
                                .TeamText(d.TeamText)
                        );
                    });
                }
            });
        };
    return {
        Teams: Teams,
        TeamTypes: TeamTypes,
        load_TeamTypes: load_TeamTypes
    };
} ();

my.TeamViewModel.load_Teams();
ko.applyBindings(my.TeamViewModel);

});

2
  • I think selectedTeam() should work. So if you use selectedTeam() with parens, then change the drop down value and check the observable's value via javascript debugger it doesn't change? If put together a JSFiddle for debugging but it's my bedtime :-( Commented Apr 18, 2013 at 3:25
  • I believe the last paragraph of my question explained how that did not work. Commented Apr 18, 2013 at 4:06

3 Answers 3

2

Is selectedTeamType set before the my.TeamViewModel.TeamTypes array is populated? If so, this could be why its initial value is not being set. I don't see anywhere in the javascript you posted where you are setting the initial value of selectedTeamType.

Update

Your answer to your own question confirmed my suspicion above. Is there a reason why you need to load the team types dropdown every time a user goes to edit a team? If the select options are the same for every team, why not just load it once?

Example:

my.TeamViewModel.load_Teams();
my.TeamViewModel.load_TeamTypes();
ko.applyBindings(my.TeamViewModel);

This way, the options list should already be loaded by the time a user clicks the edit button. It could save your client/server some sweat too, if a fresh team types options list is not needed for every team in the list.

If you want to guarantee that both $.ajax calls will complete before you do anything, I suggest looking into jQuery's $.Deferred method and promise API. When you have more than 1 $.ajax call, and you need to perform an action only after both have succeeded, you can do something like this:

var defer_Teams = $.Deferred();
var defer_TeamTypes = $.Deferred();
$.ajax({ // initiate the server call to get teams
    url: '/teams',
    success: function(data) {
        defer_Teams.resolve(data); // tell the promise API this is ready
    }
});
$.ajax({ // initiate the server call to get team types
    url: '/team-types',
    success: function(data) {
        defer_TeamTypes.resolve(data); // tell the promise API this is ready
    }
});

$.when(defer_Teams, defer_TeamTypes).then(function(teamData, teamTypeData) {

    $.each(teamTypeData, function(i, d) {
        // populate team types array
        // could also do this with ko.mapping instead of $.each
    })

    $.each(teamData, function(i, d) {
        // populate teams array
        // could also do this with ko.mapping instead of $.each
    })

});

Say your /teams call returns fast, but your /team-types call lags. With the above, the teams array would not be populated until both calls finish. And you really do need that types call to finish before the user can click the edit button and get the selected value pre-populated.

Sign up to request clarification or add additional context in comments.

Comments

1

The apparent fix involved two steps:

First, pass a reference to the current item and its current TeamType in the Edit function.

self.Edit = function () {
    var that = this; 
    var _TeamType = self.TeamTypeID();
    my.TeamViewModel.load_TeamTypes(that, _TeamType);
};

Then, and most importantly, pass those references to the function that loads the TeamTypes, and **set the selectedTeamType AFTER the TeamTypes array has loaded; in this case, the selectedTeamType is set in the complete function of jQuery's ajax.

load_TeamTypes = function (that, _TeamType) {
    $.ajax({
              // only part of ajax call displayed here for sake of brevity...              
                        $.each(data, function (i, d) {
                            TeamTypes.push(new my.TeamTypesModel()
                                    .TeamTypeID(d.TeamTypeID)
                                    .TeamTypeText(d.TeamTypeText)
                            );
                        });

                //...ajax continued
                complete: function () {
                    that.selectedTeamType(_TeamType);
                }
    });
},

9 Comments

This makes sense. If you set the selected value to an option not in the drop down, it's ignored. So I bet you were setting it, it was getting ignored because it didn't match any available option, and then when options changed, it didn't update the selected value.
What happens if the load_Teams ajax call finishes before the load_TeamTypes ajax call? Have you tried setting a delay in the load_TeamTypes server call to test this?
@danludwig - not sure I follow... The only way the load_TeamTypes can be called is if the Teams list is displayed and the button that calls the Edit function for an item in the Teams list is visible. So I can't ever see a situation where the load_TeamTypes would be called prior to load_Teams.
@mg1075 ok gotcha. You are right that you have to set the <select> element's value binding (selectedTeamType) after the options array is populated. If you try to do it before, knockout will not see the <option> in the dropdown, and it does not cache to set it after the options array gets populated. Still looks funny to set selectedTeamType during complete though. Do you still need to set it even if the ajax call fails? Can't you just that.selectedTeamType(_TeamType); after the $each loop completes? Also, shouldn't the Edit function call my.TeamViewModel.load_TeamsTypes?
@danludwig - yep, it also worked after the $each, and did not work if just before the $each. My thinking with the complete was that by setting the selected value there, it would be guaranteed to run after the $each populates the dropdown list. Also, yes, the call for Edit should be to load_TeamTypes. Sorry for the mix-up there; it's twice the work when trying to make a real case to be anonymous.
|
0

Have you tried changing your constructor to take in d like so

my.TeamTypesModel = function (data) {
        var self = this;
        self.TeamTypeID = ko.observable(data.TeamTypeId);
        self.TeamTypeText = ko.observable(data.TeamTypeText);
    };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.