James
James

Reputation: 3283

Chaining Ajax requests based results of jQuery .each only fires beforeSend once

I've modified the way Magento handles categories to allow a user to add multiple configurable products at once from the category page by chaining together ajax requests of the add to cart urls.

Basically a user selects a checkbox for which items they would like to add and this gives the <li> the product is in a class of 'active'. This jQuery grabs each <li> that has a class active and then grabs the 'add to cart URL' from the selected drop down in the variable theLink.

$j('li.active').each(function(){
            var theLink = $j(this).find('.shopthislookpageselect').val();
            var successString = "was added to your shopping cart."
            $j.ajax({
                beforeSend: function(){$j('#modalBackground').show();},
                type:"POST",
                url: theLink,
                success: function(data){
                    var returnPage = data;
                    var exists = (returnPage.indexOf(successString) !== -1);
                    if (exists) {$j('.col-main').prepend('<ul class="messages"><li class="success-msg"><ul><li><span>The items selected were added to your shopping cart. View your cart <a href="https://www.culturekings.com.au/checkout/cart/">HERE</a></span></li></ul></li></ul>'); $j("html, body").animate({ scrollTop: 0 }, "slow"); }
                    else {alert ('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.')}
                    },
                error: function(){alert('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.');},
                complete: function(){$j('#modalBackground').fadeOut(200);}
            });
});

The div modalBackground is a transparent full width and height overlay that has a loading gif which is supposed to show at the start of each ajax call, and hide at the end.

The problem is that the modalBackground is shown at the start of the first ajax call and hidden at the end, but it doesn't show again for any of the other calls afterwards. I can see this function is still running because I can count the success message that is shown at the end of each successful call of the success function.

Does beforeSend only fire once? If so why? Or should I be handling this differently for the overlay.

Note that without changing big things in Magneto's add to cart controller it can't handle adding all of the products at once so I'm stuck doing these multiple Ajax requests.

Upvotes: 0

Views: 850

Answers (2)

Matthew Graves
Matthew Graves

Reputation: 3284

Well it seems to me that you're dealing with a timing issue related to the async nature of this operation. I would suggest firing your $j('#modalBackground').show(); command once and fading it only once. So essentially you would want to do something more like:

$j('li.active').each(function(index){
            if(index===0) {
                $j('#modalBackground').show();
            }
            var theLink = $j(this).find('.shopthislookpageselect').val();
            var successString = "was added to your shopping cart."
            $j.ajax({
                type:"POST",
                url: theLink,
                success: function(data){
                    var returnPage = data;
                    var exists = (returnPage.indexOf(successString) !== -1);
                    if (exists) {$j('.col-main').prepend('<ul class="messages"><li class="success-msg"><ul><li><span>The items selected were added to your shopping cart. View your cart <a href="https://www.culturekings.com.au/checkout/cart/">HERE</a></span></li></ul></li></ul>'); $j("html, body").animate({ scrollTop: 0 }, "slow"); }
                    else {alert ('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.')}
                    },
                error: function(){alert('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.');},
                complete: function(){
                    if(index===($j('li.active').length-1)) {
                        $j('#modalBackground').fadeOut(200);
                    }
                }
            });
});

This code would prevent unnecessary shows and hides conflicting with each other, which I imagine is the issue at hand.

Remember, beforeSend will fire IMMEDIATELY after a complete event, whereas a complete has an arbitrary firing time based on the latency of the response.

Upvotes: 0

kalley
kalley

Reputation: 18462

Here's an approach:

// this will store the ajax requests with are jQuery.Deferred()
var promises = [];

// ajaxSettings.beforeSend is actually better to use to modify the xhr.
// You can just do this beforehand.
$j('#modalBackground').show();
$j('li.active').each(function(){
   // all your other stuff
   // success and error callbacks will still run for each one.
   promises.push($j.ajax({
       type:"POST",
       url: theLink,
       success: function(data){
           var returnPage = data;
           var exists = (returnPage.indexOf(successString) !== -1);
           if (exists) {$j('.col-main').prepend('<ul class="messages"><li class="success-msg"><ul><li><span>The items selected were added to your shopping cart. View your cart <a href="https://www.culturekings.com.au/checkout/cart/">HERE</a></span></li></ul></li></ul>'); $j("html, body").animate({ scrollTop: 0 }, "slow"); }
           else {alert ('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.')}
       },
       error: function(){alert('There was a problem adding these products to your cart. \n Please select the View Full Product Info link to add these items individually.');}
    }));
});

// What this does is gathers all those jQuery.Deferred and when
// all of them are done, runs the done callback.
$j.when.apply($, promises).done(function() {
    $j('#modalBackground').fadeOut(200);
});

That would all happen in the same function, so however you're calling it, do it all together.

To learn more about Deferred(): http://api.jquery.com/category/deferred-object/

Upvotes: 3

Related Questions