views:

528

answers:

3

Part of the jQuery/ajax asynchronous callback stuff still has me baffled, and I'm sure it's just because I don’t know javascript well enough.

Simplifying the code down as much as possible, this is where I'm stuck:

If I create an empty div with an id of "queuediv1" I can fill it with the results of a page method like this.

$(document).ready(function() { 
    $.ajax({ 
      type: "POST", 
      contentType: "application/json; charset=utf-8", 
      dataType: "json", 
      url: "test12.aspx/GetHtmlTest", 
      //data: "{ 'Dividend': '" + $("#Dividend").val() + "' }", 
      data: "{}", 
      // Error! 
      error: function(xhr, status, error) { 
          // Boil the ASP.NET AJAX error down to JSON. 
          //var err = eval("(" + xhr.responseText + ")"); 

          // Display the specific error raised by the server 
          //alert(err.Message); 
          alert("AJAX Error!"); 
      }, 
       success: function(msg) { 
        $("#queuediv1").removeClass('isequeue_updating'); 
        $("#queuediv1").html(msg); 
            //an unorderd list with the tag "browserxx" was just inserted into the div 
        $("#browserxx").treeview(); 
      } 
    }); 
});

This works great; it doesn't the block, and it gives me full control of error handling. However, when I try to expand this, I get into trouble. If I have several areas of the page that I want to update, I can alter the call so that each async call is made with the correct "data", but I can't tell the callback the id of the control that I want it to populate.

Simplifying my case down to something that is still broken:

Suppose there are 4 divs in the DOM that with id's queuediv1,queuediv2,queuediv3, and queuediv4 that I want to update. I would like to reuse as much code as possible. While the number and id's of divs to be updated will actually be dynamic, I thought this would have worked:

$(document).ready(function() { 
for (i= 1;i<=4;i++) 
{ 
     var divname ="#queuediv"+i; 

    $.ajax({ 
      type: "POST", 
      contentType: "application/json; charset=utf-8", 
      dataType: "json", 
      url: "test12.aspx/GetHtmlTest", 
      // data would be populated differently so that each div gets its own result- for now it doesn't matter 
      //data: "{ 'Dividend': '" + $("#Dividend").val() + "' }", 
      data: "{}", 
      // Error! 
      error: function(xhr, status, error) { 
          // Boil the ASP.NET AJAX error down to JSON. 
          //var err = eval("(" + xhr.responseText + ")"); 

          // Display the specific error raised by the server 
          //alert(err.Message); 
          alert("AJAX Error!"); 
      }, 
       success: function(msg) { 
        $(divname).removeClass('isequeue_updating'); 
        $(divname).html(msg); 
        $("#somethingfromthemsg").treeview(); 
      } 
    }); 
} 
});

But this can't ever work since by the time success is called the scope is wrong and the divname already equals "#queuediv4" for every callback. Only that div gets updated (4x). Is there a way to pass a variable to the callback? Or am I just thinking about the problem wrong.

I did find something like this dealing with async calls to $.getJSON here: http://thefrontiergroup.com.au/blog/tag/jquery

This site talked about wrapping the callback inside another anonymous function to preserve the calling variables. That sort of made sense for scope but I have no idea how to form that the way the $.ajax call is created.

+1  A: 

You can wrap every iteration of the for loop in an anonymous function like this:

$(document).ready(function() { 
for (i= 1;i<=4;i++) 
{ 
(function (){
     var divname ="#queuediv"+i; 

    $.ajax({ 
      type: "POST", 
      contentType: "application/json; charset=utf-8", 
      dataType: "json", 
      url: "test12.aspx/GetHtmlTest", 
      data: "{}", 
      error: function(xhr, status, error) { 
          alert("AJAX Error!"); 
      }, 
       success: function(msg) { 
        $(divname).removeClass('isequeue_updating'); 
        $(divname).html(msg); 
        $("#somethingfromthemsg").treeview(); 
      } 
    });
})(); 
} 
});

A much simpler example would be:

<div id="output1"></div><div id="output2"></div><div id="output3"></div><div id="output4"></div><div id="output5"></div>
<script language="javascript">
for(var a=1; a<=5; a++) {
  (function (){
    var divName = "output" + a;
    var b = a;
    setTimeout(function(){document.getElementById(divName).innerHTML = b;}, 2000);
  })();
}
</script>
Jeremy Stanley
That was exactly what I was looking for Thanks!
Jim
+1  A: 

Remember that JavaScript is a real functional language, and all functions (anonymous or not) are really full featured closures, therefore variables from 'outside' a function are still available.

a generic method for solving the 'passing parameters' is the runtime construction of closures:

function makeSuccessFunc (divname) {
    return function (msg) {
        $(divname).removeClass('isequeue_updating'); 
        $(divname).html(msg); 
        $("#somethingfromthemsg").treeview(); 
    };
};

so you can do:

$(document).ready(function() { 
for (i= 1;i<=4;i++) 
{ 
     var divname ="#queuediv"+i; 

    $.ajax({ 
      type: "POST", 
      contentType: "application/json; charset=utf-8", 
      dataType: "json", 
      url: "test12.aspx/GetHtmlTest", 
      // data would be populated differently so that each div gets its own result- for now it doesn't matter 
      //data: "{ 'Dividend': '" + $("#Dividend").val() + "' }", 
      data: "{}", 
      // Error! 
      error: function(xhr, status, error) { 
          // Boil the ASP.NET AJAX error down to JSON. 
          //var err = eval("(" + xhr.responseText + ")"); 

          // Display the specific error raised by the server 
          //alert(err.Message); 
          alert("AJAX Error!"); 
      }, 
       success: makeSuccessFunc (divname)
    }); 
} 
});
Javier

related questions