tags:

views:

248

answers:

3

I have this problem where I do an .each() on this group of selects, for each one it fires off a call to the server for some data to populate it with. However I couldn't figure out why it would only populate the bottomest one. Then I threw in some alerts() and realized it was only running the call back function on the last one multiple times. I realized that by the time the first JSON call was done, $(this) was something different... How can I get it to wait so all of them will be populated by the proper call?

HERE IS THE SCRIPT PART:

    var thisbundle;
    var testcount = 0;

 //get bundle options first..
    $("select.bundle").each(function(){

         thisbundle = $(this);
        testcount++;
         var url = "/order/getpricing/" + thisbundle.attr("id");

        //clear it out...
            //thisbundle.children().remove();

        var passbundle = thisbundle;
            $.getJSON(url, function(data, passbundle){
                var options = '';
                for(n = 0; n < data.length; n++){
                    options += '<option value="' + data[n].volumeID + '">' + explainPricing(data, n) + '</option>';

                    }
                passbundle.html(options);
        });
    });

AND HERE IS THE FORM PART:

<div id="bundles">
<table>
    <%foreach (KODmvc.Models.Product prod in Model.products)
      {%>
            <%if (prod.NumberOfCourses > 1)
              { %>
                <tr><td><img src="<%=prod.Icon %>" /></td><td><b><%=prod.Title%></b><br /><%=prod.Description%></td><td><select class="bundle" id="<%=prod.ProductID %>"><option value="-1">None</option>"</select></td></tr>
            <%} %>
    <%} %>
    </table>
</div>
+3  A: 

Enclose the ajax call in an anonymous function like this. This creates a new closure for every select element. Each of these closures will remember it's own value for passbundle.

$("select.bundle").each(function(){
    thisbundle = $(this);
    testcount++;
    var url = "/order/getpricing/" + thisbundle.val();
    alert(thisbundle.id);

    //clear it out...
    //thisbundle.children().remove();

    (function(){
        var passbundle = thisbundle;
        $.getJSON(url, function(data, passbundle){
            var options = '';
            for(n = 0; n < data.length; n++){
                options += '<option value="' + data[n].volumeID + '">' + explainPricing(data, n) + '</option>';
            }
            passbundle.html(options);
        });
    })();

});
Chetan Sastry
what if I stopped using passbundle could I still use thisbundle?
shogun
it won't work then. You see the value of thisbundle changes every time the outer loop is run. Your inner functions carry a 'live' pointer to this variable due to the scope. By the time your event handler runs, the value of `thisbundle` variable would be the last value it had.
Chetan Sastry
but I noticed that passbundle wasn't working right, saying it had no .html, I think it's because $(this) became a JSON call once inside?
shogun
Gumbo's answer has the right effect in creating a local scope. Declaring thisbundle inside function will make copies. It solves your problem in a simpler way.
Chetan Sastry
I learned something new from this one, I hadn't realized it worked like that. This could be very handy, thanks.
shogun
+1  A: 

You could also use async : false, although that might be the wrong direction to head if you are looping. But it is worth looking at.

Brandon Hansen
+2  A: 

Declare thisbundle in your function and not in the global scope:

$("select.bundle").each(function(){
    var thisbundle = $(this);
    // …
});

Otherwise the global object would be overwritten with each iteration that the callback function would then use.

Gumbo
isn't there any other way?
shogun
Changed my answer.
Gumbo
thanks, this worked, funny I tried that before and I thought it didn't work but I think that's because I found out I was referencing an attribute the wrong way so I thought the $(this) wasn't the $(this) I thought it was...
shogun
+1. Why didn't I think of this! My answer is overly complicated.
Chetan Sastry