skip to Main Content

The code below is working for add to cart with WooCommerce for simple products, but not for variable products. Can someone help me out? Because this code works with the latest WC, please do not remove this question because there are lots of codes for this online that do not work at all.

The error I am getting that is says in a notice that I should choose a product option, even when I chose one.

JS:

jQuery(function($) {
        $('form.cart').on('submit', function(e) {
            e.preventDefault();

            var form = $(this);
            form.block({ message: null, overlayCSS: { background: '#fff', opacity: 0.6 } });

            var formData = new FormData(form.context);
            formData.append('add-to-cart', form.find('[name=add-to-cart]').val() );

    var $thisbutton = form.find('.single_add_to_cart_button'); //


            // Ajax action
            $.ajax({
                url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'ace_add_to_cart' ),
                data: formData,
                type: 'POST',
                processData: false,
                contentType: false,
      beforeSend: function (response) {
           $thisbutton.removeClass('added').addClass('loading');
       },
                complete: function( response ) {
                    response = response.responseJSON;

                    if ( ! response ) {
                        return;
                    }

                    if ( response.error && response.product_url ) {
                        window.location = response.product_url;
                        return;
                    }

                    // Redirect to cart option
                    if ( wc_add_to_cart_params.cart_redirect_after_add === 'yes' ) {
                        window.location = wc_add_to_cart_params.cart_url;
                        return;
                    }

                    // Trigger event so themes can refresh other areas.
                    $( document.body ).trigger( 'added_to_cart', [ response.fragments, response.cart_hash, $thisbutton ] );

                    // Remove existing notices
                    $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove();

                    // Add new notices
                    form.closest('.product').before(response.fragments.notices_html)

                    form.unblock();
                }
            });
        });
    });

PHP:

/* WOOCOMMERCE AJAX ADD TO CART
--------------------------------------------------- */
function ace_ajax_add_to_cart_handler() {
  WC_Form_Handler::add_to_cart_action();
    WC_AJAX::get_refreshed_fragments();
}

/* Add fragments for notices. */
function ace_ajax_add_to_cart_add_fragments( $fragments ) {
    $all_notices  = WC()->session->get( 'wc_notices', array() );
    $notice_types = apply_filters( 'woocommerce_notice_types', array( 'error', 'success', 'notice' ) );

    ob_start();
    foreach ( $notice_types as $notice_type ) {
        if ( wc_notice_count( $notice_type ) > 0 ) {
            wc_get_template( "notices/{$notice_type}.php", array(
                'messages' => array_filter( $all_notices[ $notice_type ] ),
        'notices' => array_filter( $all_notices[ $notice_type ] ),
            ) );
    }
    }

    $fragments['notices_html'] = ob_get_clean();
  wc_clear_notices();
    return $fragments;
}

add_action( 'wc_ajax_ace_add_to_cart', 'ace_ajax_add_to_cart_handler' );
add_action( 'wc_ajax_nopriv_ace_add_to_cart', 'ace_ajax_add_to_cart_handler' );
remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 ); // Remove WC Core add to cart handler to prevent double-add
add_filter( 'woocommerce_add_to_cart_fragments', 'ace_ajax_add_to_cart_add_fragments' );

2

Answers


  1. To enable Ajax add to cart for simple and variable products on Single product pages you need something different, building a custom Ajax add to cart function that handles also custom product fields and cart item data manipulation (what WooCommerce default Ajax add to cart doesn’t allow).

    Try the following fully tested on WooCommerce version 4.9.0:

    add_action( 'wp_footer', 'single_product_ajax_add_to_cart_js_script' );
    function single_product_ajax_add_to_cart_js_script() {
        ?>
        <script>
        (function($) {
            $('form.cart').on('submit', function(e) {
                e.preventDefault();
    
                var form   = $(this),
                    mainId = form.find('.single_add_to_cart_button').val(),
                    fData  = form.serializeArray();
    
                form.block({ message: null, overlayCSS: { background: '#fff', opacity: 0.6 } });
    
                if ( mainId === '' ) {
                    mainId = form.find('input[name="product_id"]').val();
                }
    
                if ( typeof wc_add_to_cart_params === 'undefined' )
                    return false;
    
                $.ajax({
                    type: 'POST',
                    url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'custom_add_to_cart' ),
                    data : {
                        'product_id': mainId,
                        'form_data' : fData
                    },
                    success: function (response) {
                        $(document.body).trigger("wc_fragment_refresh");
                        $('.woocommerce-error,.woocommerce-message').remove();
                        $('input[name="quantity"]').val(1);
                        $('.content-area').before(response);
                        form.unblock();
                        // console.log(response);
                    },
                    error: function (error) {
                        form.unblock();
                        // console.log(error);
                    }
                });
            });
        })(jQuery);
        </script>
        <?php
    }
    
    add_action( 'wc_ajax_custom_add_to_cart', 'custom_add_to_cart_handler' );
    add_action( 'wc_ajax_nopriv_custom_add_to_cart', 'custom_add_to_cart_handler' );
    function custom_add_to_cart_handler() {
        if( isset($_POST['product_id']) && isset($_POST['form_data']) ) {
            $product_id = $_POST['product_id'];
    
            $variation = $cart_item_data = $custom_data = array(); // Initializing
            $variation_id = 0; // Initializing
    
            foreach( $_POST['form_data'] as $values ) {
                if ( strpos( $values['name'], 'attributes_' ) !== false ) {
                    $variation[$values['name']] = $values['value'];
                } elseif ( $values['name'] === 'quantity' ) {
                    $quantity = $values['value'];
                } elseif ( $values['name'] === 'variation_id' ) {
                    $variation_id = $values['value'];
                } elseif ( $values['name'] !== 'add_to_cart' ) {
                    $custom_data[$values['name']] = esc_attr($values['value']);
                }
            }
    
            $product = wc_get_product( $variation_id ? $variation_id : $product_id );
    
            // Allow product custom fields to be added as custom cart item data from $custom_data additional array variable
            $cart_item_data = (array) apply_filters( 'woocommerce_add_cart_item_data', $cart_item_data, $product_id, $variation_id, $quantity, $custom_data );
    
            // Add to cart
            $cart_item_key = WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation, $cart_item_data );
    
            if ( $cart_item_key ) {
                // Add to cart successful notice
                wc_add_notice( sprintf(
                    '<a href="%s" class="button wc-forward">%s</a> %d &times; "%s" %s' ,
                    wc_get_cart_url(),
                    __("View cart", "woocommerce"),
                    $quantity,
                    $product->get_name(),
                    __("has been added to your cart", "woocommerce")
                ) );
            }
    
            wc_print_notices(); // Return printed notices to jQuery response.
            wp_die();
        }
    }
    

    Code goes in functions.php file of the active child theme (or active theme). Tested and works.

    All notices are enable and displayed on the fly:

    enter image description here


    Handling product custom fields and cart item data manipulation:

    This custom Ajax add to cart code handle product custom fields, allowing to add them as custom cart item data.

    For that we use woocommerce_add_cart_item_data WooCommerce dedicated hook enabled in our custom Ajax add to cart code, allowing to add product custom fields as custom cart item data or manipulate cart data on the fly.

    If we add a product custom input text field with this code:

    add_action( 'woocommerce_before_add_to_cart_button', 'product_additional_custom_fields' );
    function product_additional_custom_fields() {
        echo '<table class="variations" cellspacing="0" width="100px">
                <tbody><tr>
                    <td class="label" style="width:100px"><label for="engraving">' . __("Engraving text") . '</label></td>
                    <td class="value">
                        <input type="text" name="engraving" id="engraving" value="" />
                    </td>
                </tr></tbody>
        </table>
        </br>';
    }
    

    Then if you want to pass the field value as custom cart item data, you will use

    add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_custom_data', 10, 5 );
    function add_cart_item_custom_data( $cart_item_data, $product_id, $variation_id = 0, $quantity, $custom_data = array() ) {
        if ( isset($custom_data['engraving']) && ! empty($custom_data['engraving']) ) {
            $cart_item_data['engraving'] = sanitize_text_field($custom_data['engraving']);
        }
    
        return $cart_item_data;
    }
    

    And you can check with the following, that will display in cart and checkout that custom cart item data:

    add_filter( 'woocommerce_get_item_data', 'display_on_cart_and_checkout', 10, 2 );
    function display_on_cart_and_checkout( $cart_data, $cart_item ) {
        if ( isset($cart_item['engraving']) ) {
            $cart_data[] = array( 
                "name" => __("Engraving text", "woocommerce"),  
                "value" => $cart_item['engraving'] 
            );
        }
    
        return $cart_data;
    }
    

    Code goes in functions.php file of the active child theme (or active theme). Tested and works.


    Notes:

    • This code works on the fly as it is, but it’s recommended to enqueue the jQuery code as an external file.

    • On some themes you should replace:
      $('.content-area').before(response); with $('.products').before(response);
      to display the notices correctly…

    Login or Signup to reply.
  2. Thanks for the helpful snippet, but the above code was not working for guest uses, but only for logins. The wc_fragment_refresh does not trigger when you log out And use it as a guest user.

    $(document.body).trigger("wc_fragment_refresh");
    

    Regards

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search