ko.ApplyBindings called before data returned

295 views
Skip to first unread message

fyfe

unread,
May 27, 2015, 6:10:17 PM5/27/15
to knock...@googlegroups.com
Hello,


I have set up my knockout objects as follows

this.ns = (function (ns) {
    ns.View = function () {
        var self, viewModel;

        self = this;
        viewModel = new ns.ViewModel();

        self.init = function () {
            console.log("Calling View Model Get Data Method");
            viewModel.getData();
            console.log("Finished Calling View Model Get Data Method");

            console.log("Started ko Bindings");
            ko.applyBindings(self, document.getElementById("View"));
            console.log("Finished ko Bindings");
        };
    };
    return ns;
}(this.ns || {}));




this.ns = (function (ns) {

    ns.ViewModel = function () {
        var self;

        self = this;

        self.Id = ko.observable();
        self.Name = ko.observable();

        self.getData = function () {
          
             $.ajax({
            url: url,
            dataType: 'json',
            type: 'POST',
            data: {},
            contentType: "application/json;charset=utf-8",
            success: function (response) {
                console.log("Started Populating ViewModel");

                self.Id(response.Id);
                self.CheckListId(response.CheckListId);
               
                console.log("Finished Populating ViewModel");
            }
        });

    };

};

    return ns;

}(this.ns || {}));


This consists of a View and ViewModel. The View calls the ViewModel getData function which retrieves the data from the server and then poulates itself.

The problem I am having is the order that the events are taking place. Looking at the console log statements I get the following

Started Log
Calling View Model Get Data Method
Started Ajax Call to Data
POST http://localhost/url.asmx/RetrieveData      200 OK   
Finished Ajax Call to Data
Finished Calling View Model Get Data Method
Started ko Bindings
Finished ko Bindings

Finished Log
Started Populating ViewModel
Finished Populating ViewModel


This seems to show that the Ajax call is causing issues with the ko.bindings  that are happening before the viewModel is populated which causes the bound data to be empty. I can confirm this by using the following in the HTML which displays as empty

<div class="layout-section tests-section" id="View" >
    <pre data-bind="text: ko.toJSON($data, {}, 2)"></pre>
</div>

Unless I move the following line
console.log("Started ko Bindings");
ko.applyBindings(self, document.getElementById("View"));
console.log("Finished ko Bindings");

to the end of the sucess ajax call at which point everything works as expected, but I do not want to have my ko.applyBindings call in the ViewModel.

I have had a look online and can not find any references to anyone else having the same issue, but did find the following on a similar topic suing computeds, but my returned data is not a computed
https://github.com/knockout/knockout/wiki/Asynchronous-Dependent-Observables

I have also looked at https://api.jquery.com/promise/

but wondered if anyone has had similar issues or could suggest any other techniques, URLs I could use to allow the Ajax call to complete before the ko.applyBindings is called? Also not sure if there are any built in Knockout methods I could use to deal with this?

Gunnar Liljas

unread,
May 27, 2015, 6:28:34 PM5/27/15
to knock...@googlegroups.com
Since $.ajax is asynchronous, so is getData. To be correct, your

console.log("Finished Calling View Model Get Data Method");

..should be inside the success callback.

If you really don't want to bind before the data has been retrieved (the most common thing would be to allow it to run in the background, and possibly show a "loading..." message while it's doing that), you will have to do this:

1. Change

$.ajax({

to

return $.ajax({

2. Change to

viewModel.getData().done(function(){
   ko.applyBindings(self, document.getElementById("View"));  //shouldn't "self" be "viewModel"?
});



--
You received this message because you are subscribed to the Google Groups "KnockoutJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to knockoutjs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Haug

unread,
May 27, 2015, 6:33:44 PM5/27/15
to knock...@googlegroups.com
I would typically throw any bindings dependent on the viewmodel being populated within an if binding. If is more detrimental to performance, so if that causes UI slowness, I would suggest throwing in a empty object with only the necessary properties to make a visible binding function properly.

fyfe

unread,
May 28, 2015, 5:09:00 AM5/28/15
to knock...@googlegroups.com
Thank you fro your suggestions and for replying so quick.

I had a look in to the deferred option, and think I will probably use that.

For anyone else with the same issues I have found a few good articles on the topic
http://blog.greatrexpectations.com/2012/06/26/command-pattern-with-jquery-deferred-knockout/
http://www.htmlgoodies.com/beyond/javascript/making-promises-with-jquery-deferred.html

You were correct Gunnar I should have had

ko.applyBindings(viewModel, document.getElementById("View"));

not

ko.applyBindings(self, document.getElementById("View"));

I must have updated it when I posted the code.


Reply all
Reply to author
Forward
0 new messages