nielsv
nielsv

Reputation: 6800

Javascript function gets undefined

this is what I have:

function loadGraphs(datawijk){

    $.ajax({                                      
      url: './api5.php',                  //the script to call to get data          
      data: {
       wijk: datawijk,
        },                        //you can insert url argumnets here to pass to api.php
                                       //for example "id=5&parent=6"
      dataType: 'json',                //data format      
      success: function(rows)          //on recieve of reply
      {
        var htmlContent = "";
        // ADD TO 

        htmlContent += '<tr><th scope="row">Geboortes</th>';

        $.each(rows, function(i, data) {
          $.each(data, function(j, year) {
            htmlContent += 
              '<td>' + year + '</td>';
          });
        });

        htmlContent += '</tr>';

        var lol = updateOverlijdens(datawijk, htmlContent);
        alert(lol);

        $('#graphs table tbody').html(htmlContent);
      }   
    });
}

function updateOverlijdens(datawijk, htmlContent){

    $.ajax({                                      
      url: './api4.php',                  //the script to call to get data          
      data: {
       wijk: datawijk,
        },                        //you can insert url argumnets here to pass to api.php
                                       //for example "id=5&parent=6"
      dataType: 'json',                //data format      
      success: function(rows)          //on recieve of reply
      {
        // ADD TO 

        htmlContent += '<tr><th scope="row">Overlijdens</th>';

        $.each(rows, function(i, data) {
          $.each(data, function(j, year) {
            htmlContent += 
              '<td>' + year + '</td>';
          });
        });

        htmlContent += '</tr>';

        return htmlContent;
      }   
    });
}

When I do alert(lol); in function loadGraphs I get undefined ... And when I do alert(htmlContent); in function updateOverlijdens just before I return the value I get it right. Only when I alert the value in my function loadGraphs I get undefined. How can I fix this?

Upvotes: 1

Views: 214

Answers (3)

Mattias Buelens
Mattias Buelens

Reputation: 20159

As explained by a lot of answers, the problem is that you're trying to return something from the AJAX call back to the function which initiated the call. This cannot work, since the caller does not wait for the AJAX call to complete and will return before it is completed. That's the whole idea about asynchronous calls: you don't want to block the execution to wait for the results to come in.

One way to handle this is by passing a callback function which needs to be executed when the results are retrieved. This function will then be called from within the AJAX success callback.

Another interesting approach is to make use of jQuery's deferreds and promises. A promise represents some value which will be retrieved somewhere in the future. A deferred produces a promise and resolves it later on. For example, all AJAX functions return a promise and you can retrieve a promise from any jQuery object which is resolved when all animations are completed.

In your case, you can create a deferred which is resolved with the htmlContent when the AJAX results are retrieved. You return the promise of that deferred from the function so the caller can bind callbacks to it or combine it with other promises.

function updateOverlijdens(datawijk) {
    // Create the deferred
    var dfd = new jQuery.Deferred();
    // Initiate the AJAX request
    $.ajax({                                      
        url: './api4.php',
        data: {
            wijk: datawijk,
        },
        dataType: 'json',
        success: function(rows) {
            var htmlContent = '<tr><th scope="row">Overlijdens</th>';

            $.each(rows, function(i, data) {
                $.each(data, function(j, year) {
                    htmlContent += '<td>' + year + '</td>';
                });
            });

            htmlContent += '</tr>';

            // Resolve the deferred
            dfd.resolve(htmlContent);
        },
        error: function() {
            // An error occurred, reject the deferred
            dfd.reject();
        }
    });
    // Return a promise
    return dfd.promise();
}

BEGIN EDIT

Thanks to Benjamin Gruenbaum for pointing out my usage of the deferred anti-pattern. Here's an implementation using .then to do the chaining:

function updateOverlijdens(datawijk) {
    return $.ajax({                                      
        url: './api4.php',
        data: {
            wijk: datawijk,
        },
        dataType: 'json'
    }).then(function(rows) {
        var htmlContent = '<tr><th scope="row">Overlijdens</th>';

        $.each(rows, function(i, data) {
            $.each(data, function(j, year) {
                htmlContent += '<td>' + year + '</td>';
            });
        });

        htmlContent += '</tr>';

        return htmlContent;
    });
}

END EDIT

You can then use the promise in your loadGraphs AJAX success callback like so:

// Produce the table header
var htmlContent = '<tr><th scope="row">Geboortes</th>';
$.each(rows, function(i, data) {
    $.each(data, function(j, year) {
        htmlContent += '<td>' + year + '</td>';
    });
});

var promise = updateOverlijdens(datawijk);
promise.then(function(htmlOverlijdens) {
    // Append to the previously created HTML content
    htmlContent += htmlOverlijdens;
    // Apply the HTML
    $('#graphs table tbody').html(htmlContent);
});

The advantage of using promises is that they give caller much more flexibility. The caller can easily register multiple callbacks using then(), combine it with other promises using jQuery.when() or pipe the results to another promise using pipe() then.

Upvotes: 1

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

The reason you are getting undefined is that you are not returning anything from updateOverlijdens (but from the inner function in success)

You are running asynchronous code, generally you can't solve this problem easily using the current function return pattern you are using since the data will not be available at the time of the return. What you need are callbacks.

Please see this page for good usage examples of jQuery ajax. What you'll be wanting to do is pass a function to updateOverlijdens and call it when the success callback of your ajax fires. There are plenty of examples for that online (and in the link above).

Here for example is your code after being modified to the correct callback pattern which addresses the problem it had with asyncrhonous execution

function loadGraphs(datawijk){

    $.ajax({                                      
      url: './api5.php',                  //the script to call to get data          
      data: {
       wijk: datawijk,
        },                        //you can insert url argumnets here to pass to api.php
                                       //for example "id=5&parent=6"
      dataType: 'json',                //data format      
      success: function(rows)          //on recieve of reply
      {
        var htmlContent = "";
        // ADD TO 

        htmlContent += '<tr><th scope="row">Geboortes</th>';

        $.each(rows, function(i, data) {
          $.each(data, function(j, year) {
            htmlContent += 
              '<td>' + year + '</td>';
          });
        });

        htmlContent += '</tr>';
        updateOverlijdens(datawijk, htmlContent,function(lol){
            alert(lol);
            $('#graphs table tbody').html(lol);
        });

      }   
    });
}

function updateOverlijdens(datawijk, htmlContent,callback){

    $.ajax({                                      
      url: './api4.php',                  //the script to call to get data          
      data: {
       wijk: datawijk,
        },                        //you can insert url argumnets here to pass to api.php
                                       //for example "id=5&parent=6"
      dataType: 'json',                //data format      
      success: function(rows)          //on recieve of reply
      {
        // ADD TO 

        htmlContent += '<tr><th scope="row">Overlijdens</th>';

        $.each(rows, function(i, data) {
          $.each(data, function(j, year) {
            htmlContent += 
              '<td>' + year + '</td>';
          });
        });

        htmlContent += '</tr>';

        callback(htmlContent)
      }   
    });
}

Upvotes: 4

ncn corp
ncn corp

Reputation: 111

Yes. There no mistake. You need to have return value for your function definied not in $.ajax. In your code you returning value to $.ajax, not to updateOverlijdens. Right code need to be:

function updateOverlijdens(datawijk, htmlContent){    
    $.ajax({ ....}) 
    return 'some value' 
}

Upvotes: 0

Related Questions