Search code examples
phpjavascriptajaxmagentojquery-forms-plugin

Multiple AJAX forms with jQuery Form


Currently, I'm using the jQuery Form plugin to handle multiple AJAX forms on a Magento product listing via .ajaxForm({});. Now, the solution I'm using works, but it is super clunky and would love to know if there is a better way to approach this problem.

I will shorten my code a bit for brevity:

<?php foreach ($_productCollection as $_product): ?>
<form action="<?php echo $this->getSubmitUrl($_product) ?>" method="post" class="derp" id="derp-<?php echo $_iterator; ?>">
    <div class="quanitybox">
        <label for="qty"><?php echo $this->__('Quantity:') ?></label>
        <input type="button" class="quantity_box_button_down" />         
        <input type="text" name="qty" maxlength="12" value="1" title="<?php echo $this->__('Qty') ?>" class="input-text qty" />
        <input type="button" class="quantity_box_button_up" />
    </div>
    <button type="submit" title="Add to Cart" class="button btn-cart" ><span><span>Add to Cart</span></span></button>
 </form>

 <script type="text/javascript">
    var productAddToCartForm = new VarienForm('derp-<?php echo $_iterator ?>');

    jQuery('#derp-<?php echo $_iterator ?>').ajaxForm({
                url: jQuery('#derp-<?php echo $_iterator ?>').attr('action').replace("checkout/cart","ajax/index"),
                data: {'isAjax':1},
                dataType: 'json',
                beforeSubmit: function(){    
                    if(productAddToCartForm.validator.validate()){
                        windowOver.show();
                        windowBox.show().css({
                            backgroundImage: "url('<?php echo $this->getSkinUrl('images/loading.gif')?>')"
                        });                    
                    }else{
                        return false;
                    }
                },
                error: function(data){
                    windowBox.css({
                          backgroundImage: 'none'
                    }).html('<?php echo $this->__('Error') ?>');                       
                    windowOver.one('click',function(){
                        hidewindow(windowBox,windowOver);                    
                    });        

                    jQuery('#hidewindow').click(function(){
                        hidewindow(windowBox,windowOver);                    
                    });
                },
                success : function(data,statusText,xhr ){
                    if(data.status == 'SUCCESS'){
                        if(jQuery('.block-cart')){
                            jQuery('.block-cart').replaceWith(data.sidebar);
                        }
                        if(jQuery('.header .block-cart-header')){
                            jQuery('.header .block-cart-header').replaceWith(data.topcart);
                        }     
                        msgHtml = '<div class="added-content"><div style="float:left;">' + productImg + '</div><em>' + titleForBox<?php echo $_iterator ?> + '</em> <?php echo $this->__('was successfully added to your shopping cart.') ?><div style="clear:both;"></div><a id="hidewindow" href="javascript:void(0);"><?php echo $this->__('Continue shopping') ?></a>&nbsp;<a href="<?php echo $this->getUrl('checkout/cart')?>"><?php echo $this->__('View cart & checkout') ?></a></div>';             
                    }else{
                        msgHtml = '<div class="added-content"><p class="error-msg" style="margin-bottom:15px;">' + data.message + '</p><a id="hidewindow" href="javascript:void(0);"><?php echo $this->__('Continue shopping') ?></a>&nbsp;<a href="<?php echo $this->getUrl('checkout/cart')?>"><?php echo $this->__('View cart & checkout') ?></a></div>';
                    }                      

                    windowBox.css({
                          backgroundImage: 'none'
                    }).html(msgHtml);                      
                    windowOver.one('click',function(){
                        hidewindow(windowBox,windowOver);                    
                    });        

                    jQuery('#hidewindow').click(function(){
                        hidewindow(windowBox,windowOver);                    
                    });    
                }
            });
</script>
<?php endforeach; ?>

The unfortunate part of this code is I'm generating a bunch of duplicate JavasScript at the bottom of each product.

I don't really see a way around this because I need to generate a new VarienForm() for each product for validation, which only takes a form id (unless I'm mistaken).

I'm doing this using the built in $_iterator, which increments per foreach() loop (ie: derp-1, derp-2, derp-3), and adding an incrementing id for each form.

I know that I can target each form by using a class selector, such as jQuery('.derp').ajaxForm({}); However I'd still need to be able to correlate this to a VarienForm.

I tried to generate the ajaxForm({}); on the fly based off of the submit button .each( function({ jQuery(this).on('click', function({ //AJAX STUFF HERE }) ); }) ); but that did not work.

Is there a more robust solution that will target each form independently, generate a VarienForm, grab any of that forms data I need, utilize the ajaxForm({}) method, and keep it all together?


Solution

  • I would create a function that you declare once on the page. It would be used by calling it in every PHP loop iteration, by passing a few parameters that are specific to the iteration (since as you pointed out, a lot of the Javascript is duplicated). For example, the function could be:

    function setupForm(form, iterator) {
        jQuery("#derp-" + iterator).ajaxForm({
    
        });
    }
    

    And replace every instance of your PHP code that prints the iterator with just a Javascript concatenation of iterator.

    You would replace the script in the PHP loop with something like this:

    var productAddToCartForm = new VarienForm('derp-<?php echo $_iterator ?>');
    setupForm(productAddToCartForm, '<?php echo $_iterator ?>');
    

    Of course, you'd have to add more arguments to the setupForm function that is specific to the loop (I will look through the code more to try to determine what they all are). That way, you make all PHP loop items print once (in the setupForm function call), and the use of them dynamic inside the setupForm function.

    UDPATE:

    I went through it and hopefully found everything that would need replaced, so I will paste the code after this little blurb. My concern is what these variables are:

    windowBox
    windowOver
    productImg
    titleForBox
    

    If they are global or something, then this new update should be fine. If they are in some other context (somehow), then this might not work.

    Anyways, here's what I think it might end up like:

    // Use this in your PHP loop
    var productAddToCartForm = new VarienForm('derp-<?php echo $_iterator ?>');
    setupForm(productAddToCartForm, "<?php echo $_iterator ?>", "<?php echo $this->getSkinUrl('images/loading.gif')?>", "<?php echo $this->__('Error') ?>", "<?php echo $this->__('was successfully added to your shopping cart.') ?>", "<?php echo $this->__('Continue shopping') ?>", "<?php echo $this->getUrl('checkout/cart')?>", "<?php echo $this->__('View cart & checkout') ?>");
    
    // Use this one time in your page
    function setupForm(form, iterator, skin_url, an_error, successful_text, continue_text, checkout_url, checkout_text) {
        var the_form = jQuery("#derp-"+iterator);  // Added this (obviously)
    
        the_form.ajaxForm({  // Changed this line
            url: the_form.attr('action').replace("checkout/cart","ajax/index"),  // Changed this line
            data: {'isAjax':1},
            dataType: 'json',
            beforeSubmit: function(){    
                if(form.validator.validate()){  // Changed this line
                    windowOver.show();
                    windowBox.show().css({
                        backgroundImage: "url('" + skin_url + "')"  // Changed this line
                    });                    
                }else{
                    return false;
                }
            },
            error: function(data){
                windowBox.css({
                      backgroundImage: 'none'
                }).html(an_error);  // Changed this line
    
                windowOver.one('click',function(){
                    hidewindow(windowBox,windowOver);
                });
    
                jQuery('#hidewindow').click(function(){
                    hidewindow(windowBox,windowOver);                    
                });
            },
            success : function(data,statusText,xhr){
                var msgHtml = "";  // Added this line
                if(data.status == 'SUCCESS'){
                    if(jQuery('.block-cart')){
                        jQuery('.block-cart').replaceWith(data.sidebar);
                    }
                    if(jQuery('.header .block-cart-header')){
                        jQuery('.header .block-cart-header').replaceWith(data.topcart);
                    }
                    msgHtml = '<div class="added-content"><div style="float:left;">' + productImg + '</div><em>' + titleForBox + iterator + '</em> ' + successful_text + '<div style="clear:both;"></div><a id="hidewindow" href="javascript:void(0);">' + continue_text + '</a>&nbsp;<a href="' + checkout_url + '">' + checkout_text + '</a></div>';  // Changed this line
                }else{
                    msgHtml = '<div class="added-content"><p class="error-msg" style="margin-bottom:15px;">' + data.message + '</p><a id="hidewindow" href="javascript:void(0);"><?php echo $this->__('Continue shopping') ?></a>&nbsp;<a href="' + checkout_url + '">' + checkout_text + '</a></div>';  // Changed this line
                }
    
                windowBox.css({
                      backgroundImage: 'none'
                }).html(msgHtml);
                windowOver.one('click',function(){
                    hidewindow(windowBox,windowOver);
                });
    
                jQuery('#hidewindow').click(function(){
                    hidewindow(windowBox,windowOver);
                });
            }
        });
    }
    

    Mainly look at the comments to see where I made changes to your actual code.