tags:

views:

64

answers:

7

Hi all,

I have a function runAjax that functions correctly. Unfortunately I am struggling to return the value I get from the ajax query.

The ajax function assigns the returned value inside "contents" or "error" xml tags to the variable "result".

If I alert the result variable inside the ajax function it alerts the correct value (i.e if the xml value inside contents is "published" it alerts published).

However if I alert the returned value from the runAjax function it alerts an object instead of the value of the internal variable "result" which in the above example is "published".

function runAjax (data_obj){
  return $.ajax({
      url:"/ajax.php",
      dataType: "xml",
      data: data_obj,
      success: function(data) {
        // format result
        var xml;
        if (typeof data == "string") {
          xml = new ActiveXObject("Microsoft.XMLDOM");
          xml.async = false;
          xml.loadXML(data);
        } else {
          xml = data;
        }
        var result;
        if($("error",xml).text()){
          result = [$("error",xml).text()];
        } else{
          result = [
            $("contents", xml).text()
          ];
        }
      alert(result); //alerts the correct string for example "published"
      return result;
      }
    });
  }
  $('ul.content li span.changeable').click(function(e){
    e.preventDefault();
    var method_set = $(this).parent().attr("class");
    var id_set = $(this).parent().parent().find('li.id span').html();
    var user = $(this);
    var result = runAjax({method: method_set, id: id_set});
    alert(result); //alerts an object not published

  });

Im sure it has something to do with the way I am returning the variable but I can't figure it out. Any input would be much appreciated.

Regards Luke

UPDATE: This is the revised code that works thanks to all the input from people below:

  function runAjax (data_obj,callback){
    $.ajax({
      url:"/ajax.php",
      dataType: "xml",
      data: data_obj,
      success: function(data) {
        // format result
        var xml;
        if (typeof data == "string") {
          xml = new ActiveXObject("Microsoft.XMLDOM");
          xml.async = false;
          xml.loadXML(data);
        } else {
          xml = data;
        }
        var result;
        if($("error",xml).text()){
          result = [$("error",xml).text()];
        } else{
          result = [
          $("contents", xml).text()
          ];
        }
        if ( typeof(callback) == "function") {
          callback(result);
        }
      }
    });
  }
  $('ul.content li span.changeable').click(function(e){
    e.preventDefault();
    var method_set = $(this).parent().attr("class");
    var id_set = $(this).parent().parent().find('li.id span').html();
    var user = $(this);
    runAjax({
      method: method_set, 
      id: id_set
    },
    function(result){
      $(user).html(result.join('')); //this is instead of alert(result);
    }
    );

  });
A: 

try removing [ and ]

  if($("error",xml).text()){
      result = $("error",xml).text();
    } else{
      result = $("contents", xml).text();
    }
Reigel
Hi, thanks for the answer, unfortunately this didn't work.
Luke
+1  A: 

Luke,

I think you're assigning the retrun value at the wrong point in the function, you should really have a single exit point before the final curly brace. you're returning the result technically as a return value of the $.ajax() function (an XMHTTP object), NOT the parent method.

try this instead:

function runAjax (data_obj){
    var returnValue;
    $.ajax({
        url:"/ajax.php",
        dataType: "xml",
        data: data_obj,
        success: function(data) {
            // format result
            var xml;
            if (typeof data == "string") {
                xml = new ActiveXObject("Microsoft.XMLDOM");
                xml.async = false;
                xml.loadXML(data);
            } else {
                xml = data;
            }
            var result;
            if($("error",xml).text()){
                result = [$("error",xml).text()];
            } else{
                result = [
                $("contents", xml).text()
                ];
            }
            alert(result); //alerts the correct string for example "published"
            returnValue = result;
        }
    });
    return returnValue;
}
$('ul.content li span.changeable').click(function(e){
    e.preventDefault();
    var method_set = $(this).parent().attr("class");
    var id_set = $(this).parent().parent().find('li.id span').html();
    var user = $(this);
    var result = runAjax({method: method_set, id: id_set});
    alert(result); //alerts an object not published

});
jim
Hi Jim, I just had a go and unfortunatly I got the same result as my original code
Luke
Luke, i actually see why it wont work in the above case either. basically, the $.ajax() method is async, so the return value will be returned before the success call. i'll drop a note of the 'pattern' that i use for this kinda thing in a seperate note..
jim
That's because var result = runAjax() is assigning a reference to an XMLHttpRequest object - NOT the value that you want.
belugabob
+1  A: 

The reason that you can't get the result returned correctly is because of the asynchronous nature of AJAX (that's what the first 'A' stands for).

The call to runAjax() probably returns long before the AJAX operation completes and the 'success' handler is invoked. The runAjax() call returns a referenc to the XMLHttpRequest object that was used to invoke the AJAX communication. The return value from the success handler cannot be used by you directly, as it returned to the internal working of the $.ajax() code.

A suitable solution would depend on what you want to do with 'result' - I'm guessing that 'alert(result)' is only for illustration purposes.

belugabob
Is there a way for me to wait for the ajax response?
Luke
Yes - that's what the 'success' function is for - it gets invoked when the AJAX response is supplied.
belugabob
Hi Belugabob, with the returned value I am replacing some html content, however I am calling the runAjax function from multiple other functions not just the one in my code example above, so I need the value returned rather than the runAjax function doing the html replacing. Does that make sense? Sorry if its unclear.
Luke
If you need each call to runAjax() to respond differently, pass in the function to use as the success handler, rather than including it inline. function runAjax(data_obj, successHandler){ return $.ajax({ url:"/ajax.php", dataType: "xml", data: data_obj, success: successHandler }); } runAjax(someData, function firstFunction(data){}); runAjax(someOtherData, function secondFunction(data){});
belugabob
belugabob - snap, see obove (or is that below) :)
jim
It's worth getting a good understanding of how to use functions as callback handlers, as this is not only important for AJAX calls, but is fundamental to the way that jQuery works in general. It took me a while to get my head round it, but once it clicked, it made a massive difference to the way that I write javascript.
belugabob
+4  A: 

According to the docs

The $.ajax() function returns the XMLHttpRequest object that it creates.

Any return value that you return from the success callback function is ignored.

You need to put the value in a variable defined in a wider scope than inside the callback function (global, or preferably inside an outer function).

   var result;
   $.ajax({
       ....
       success : function(data) {
          ...
          result = ...;
       }
   });

Or better yet: do whatever you want to do with the return value inside the success callback function, this will keep the asynchronous nature of the ajax call and means you don't need to wait for the call to come back.

Doing your processing in the success callback function means you know you have the results, if you put the value in a variable the variable may not be assigned a value yet by the time you want to use it.

In a comment to another answer on this page you say:

however I am calling the runAjax function from multiple other functions not just the one in my code example above, so I need the value returned rather than the runAjax function doing the html replacing

I would add an extra parameter to your runAjax function, which is another callback function that you can pass in different processing functions from the various functions.

function runAjax(data_obj, callback) {
    $.ajax({
        ...
        success : function(data) { 
            ...
            result = ...
            ...
            if ( typeof(callback) == "function") {
                callback(result);
            }
        }
    });
}

Then you can call it like

runAjax({method: method_set, id: id_set},
    function(result){
         alert(result);
    }
);

Then you can do your generic processing of the data in the success function, but the custom processing for each call in the callback function.

If you really need to wait for the call, you can create a synchronous ajax call by passing the async option:

 $.ajax({
    async:false,
    ....
Mario Menger
I didn't consider the use of a synchronous call, as it's usually inappropriate, but it could be a valid way of handling things if there is no other option. Depends what the post AJAX processing requirments are.
belugabob
Thanks Mario, the last edit you made just edged your answer to be the chosen one as I didnt want to have to write the whole success function out each time I called the function. Thanks very much works a charm.
Luke
+2  A: 

Luke,

Basically, create a wrapper function for your $.ajax() call with a parameter for the callback portion (you could of course have parameters for any valid paramter in the ajax call. here's a quickie to demonstrate:

function runAjax (data_obj, callback){
    $.ajax({
        url:"/ajax.php",
        dataType: "xml",
        data: data_obj,
        success: function(data) {
            if (data != null && callback !== null ) {
                callback(data);
            }
        }
    });
}

function callbackFunction (data) {
    // format result
    var xml;
    if (typeof data == "string") {
        xml = new ActiveXObject("Microsoft.XMLDOM");
        xml.async = false;
        xml.loadXML(data);
    } else {
        xml = data;
    }
    var result;
    if($("error",xml).text()){
        result = [$("error",xml).text()];
    } else{
        result = [
        $("contents", xml).text()
        ];
    }
    alert(result); //alerts the correct string for example "published"
    // do your DOM updates etc here
}

$('ul.content li span.changeable').click(function(e){
    e.preventDefault();
    var method_set = $(this).parent().attr("class");
    var id_set = $(this).parent().parent().find('li.id span').html();
    var user = $(this);
    runAjax({method: method_set, id: id_set}, callbackFunction);
});

hope this helps..

jim
Great minds think alike. (And John Resig certainly has a great mind) :-)
belugabob
Thanks Jim, that was a great help, I just didnt want to declare a whole big function like your callbackFunction for every runAjax call, +1 for the help though, thanks
Luke
luke - for completeness, could you update your question with the revised code (as an update below it) as it'll be useful for folks to see the 'before and after' - thanks..jim
jim
All done :) thanks again
Luke
no worries - cheers luke...
jim
A: 

Luke, To further refine the answers provided by myself and Jim, considering that the result parsing bit is likely to be consistent for each of the different calls...

function runAjax (data_obj, callback){ 
    $.ajax({ 
        url:"/ajax.php", 
        dataType: "xml", 
        data: data_obj, 
        success: function(data) { 
            if (data != null && callback !== null ) { 
                // format result 
                var xml; 
                if (typeof data == "string") { 
                    xml = new ActiveXObject("Microsoft.XMLDOM"); 
                    xml.async = false; 
                    xml.loadXML(data); 
                } else { 
                    xml = data; 
                } 
                var result; 
                if($("error",xml).text()){ 
                    result = [$("error",xml).text()]; 
                } else{ 
                    result = [$("contents", xml).text()]; 
                } 
                callback(result); 
            } 
        } 
    }); 
} 

function callbackFunction (result) { 
    alert(result); //alerts the correct string for example "published" 
    // do your DOM updates etc here 
} 

$('ul.content li span.changeable').click(function(e){ 
    e.preventDefault(); 
    var method_set = $(this).parent().attr("class"); 
    var id_set = $(this).parent().parent().find('li.id span').html(); 
    var user = $(this); 
    runAjax({method: method_set, id: id_set}, callbackFunction); 
}); 
belugabob
Hi Belugabob, this is basically what I ended up doing but the callbackFunction is not the same for each runAjax call so I assign the function in the call rather than globally like yours is, thanks very much for the input, was very useful.
Luke
Luke - as a final aside. i would definately always try and keep the callback as a seperate function (probably, the above is a perfect implementation). the reason being that you may want to process the same data from a non ajax call to do basically the same processing. therefore you'd keep it DRY.
jim
Hi Jim, in my revised code you can see I have used the "user" variable, how could I use this variable if I were to create the function globaly, only because I might not always want to use "user" so I dont want to have to parse "user" as a variable to runAjax.
Luke
Luke, i see your point. tho of course, in that case you could always change the sigature to : function callbackFunction (result, user) and in the runAjax code :function runAjax (data_obj, callback, user), follwed by: callback(result, user); i know it's convoluted but you get my drift :) (i appreciate that you may not always wish to pass user to runAjax of course..)
jim
A: 

You can handle the success of the ajax call as an event which keeps the good stuff of the async call including stuff based on the url you called.

$('ul.content li span.changeable').ajaxSuccess(function(e, xhr, settings) {
  if (settings.url == '/ajax.php') {
    $(this).text('Triggered ajaxSuccess handler.');
    someglobalresultvariable = xhr.responseXML; // the xml of the response
    $(this).text(someglobalresultvariable);
  }
});

Here I make the wild assumption that you want to change the span content text based on the clicked item.

NOTE: I replaced the "triggered" text message and only show that as an example. You can decide how you wish to handle the result.

Mark Schultheiss