0

I have a Knockout binding to my function:

<tr id="toolbarRow" data-bind="foreach: get_tabs()">

get_tabs calls load which uses an ajax request to populate the departments variable:

get_tabs = function () {
    load();
    return departments;
},

This causes me a problem as get_tabs returns before departments is populated by load.

Here's load:

load = function () {
    $.ajax(
            {
                url: _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext='Department:*"
                    + "*'&selectproperties='Department'&sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&sortlist='Department:ascending'",
                method: "GET",
                headers: {
                    "accept": "application/xml",
                },
                success: onSuccess,
                error: onError
            }
        );
},

onSuccess = function (data) {
    ...populating departments variable...
},

onError = function (err) {
    alert("something blew up");
},

How can I keep get_tabs from returning until my ajax request finishes the onSuccess event?

tnw
  • 13,521
  • 15
  • 70
  • 111
  • 3
    Ajax is, by nature, asynchronous. You need to add callback handlers to properly get at the data coming back on the http response. What does your load() method look like? – jgitter Feb 14 '14 at 20:05
  • `departments` should return on success call of Ajax – Suman Bogati Feb 14 '14 at 20:06
  • @SumanBogati Can you elaborate? I'm not sure how that would look in my code. – tnw Feb 14 '14 at 20:06
  • After request the ajax, you will get the some response, so you need to return `departments` on successful response from ajax. – Suman Bogati Feb 14 '14 at 20:08
  • @SumanBogati That's more or less exactly what you said before, I still don't know how that would look in my code. If you would like to add that code as an answer, I would gladly accept it if it works. – tnw Feb 14 '14 at 20:09
  • Think about it as if more than one process are running at the same time. When you do an ajax call, the function will continue running normally on one line, while the ajax request will run on the background. Therefore the function will return immediately, and later the server will return. Here, check this out-> http://www.javalobby.org/articles/ajax/ajax-fig2_small.png – Mohammed Joraid Feb 14 '14 at 20:32
  • @Joraid Thanks, I know what asynchronous means already though. – tnw Feb 14 '14 at 20:33
  • ops, my bad. I was looking into someones else question. Just now noticed that you have knockout.js in your tags, and 4k SO points :D – Mohammed Joraid Feb 14 '14 at 20:36

4 Answers4

2

As the comments point out, you can't expect to be able to return data from an AJAX request. Typically you provide a callback function that does work after the result is retrieved.

The correct way to do this when using KnockoutJS is to use an observable array. Declare a property as an observable array in your viewmodel, data-bind some HTML to it, and then populate the data when you need to. Here's a simple example:

function MyViewModel() {
    this.departments = ko.observableArray([]);
    this.load = function () {
        $.ajax({
            /* your AJAX options */
        })
        .success(this.departmentsLoaded.bind(this))
    };

    this.departmentsLoaded = function (data) {
        this.departments(data);
    };
}

Your view/markup would look something like this:

<div data-bind="foreach: departments">
    <span data-bind="text: $data"></span>
</div>
<button data-bind="click: load">Load</button>

Example: http://jsfiddle.net/CCNtR/113/

Community
  • 1
  • 1
Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
1

You're getting warm. $.ajax returns immediately. What you need to do is use your success handler in the $.ajax call to react to the data that is returned by the server.

Here's what I would do:

get_tabs = function(callback) {
    load(callback);
}
load = function(callback) {
    $.ajax({
        ...,
        success: function(data) {
            // populate departments data
            callback.apply(this, arguments);
        }
    });
}
jgitter
  • 3,396
  • 1
  • 19
  • 26
  • Please clarify your second sentence, like I said I don't know how that would look in my code. – tnw Feb 14 '14 at 20:15
  • Code sample added. Your callback would handle rendering any additional UI elements. I recommend the use of client-side templates. – jgitter Feb 14 '14 at 20:19
  • Still confusing. What am I passing in on my KO binding for `callback`? You also call `load(callback)` but `load` doesn't take any arguments. – tnw Feb 14 '14 at 20:20
  • You're right, I forgot to add the argument - I was typing quickly. I don't know how your application is put together, but if you have Knockout binding to an object and that object changes - your work should already be done. – jgitter Feb 14 '14 at 20:24
1

Why not make departments an observable array and bind directly to that? No callback required. You'd do something like:

 <tr id="toolbarRow" data-bind="foreach: departments">

function myViewModel()
{
    var self = this;

    self.departments = ko.observableArray();

    //call the following in your document ready 
    myViewModel.prototype.load = function () 
    {
        $.ajax(
        { //make your ajax call


            success: function (result,status,xhr)
            {
                ...populating departments variable...
            }
        });   
    }
}

$(document).ready(function()
{
    //Toolbar will be empty because departments is empty at this point.
    ko.applyBindings(myViewModel);

    //Your onSuccess handler will populate the departments observable array
    //when it changes, your toolbar will populate. No need for callbacks.
    myViewModel.load();
}

This is really the beauty of knockoutjs is that you don't have to worry about handling stuff like this...much ;-)

Chuck Schneider
  • 344
  • 2
  • 6
  • Thanks kindly for your response, Chuck. – tnw Feb 17 '14 at 15:12
  • You are welcome! Thanks for the up vote :-). Is there a design reason why you are binding to a function instead of directly to an observable array? BTW, this exactly what I'm already doing in my code with ajax calls. – Chuck Schneider Feb 18 '14 at 06:12
-2

You need to return departments inside onSuccess() function

 onSuccess = function (data) {
    return departments;
},
Suman Bogati
  • 6,289
  • 1
  • 23
  • 34
  • How would I change my `get_tabs` and `load` function to accommodate this? – tnw Feb 14 '14 at 20:14
  • This is... awful. The success handler won't be returning anything. You should be using the handler to redraw a portion of your UI, or fire an event. – jgitter Feb 14 '14 at 20:14