13

In Magento 2 the normal Add to Cart button has a very nice AJAX-based logic to add products to the cart.
However, when we use a Catalog Products List widget for example in CMS pages the button in this products list works with an HTTP POST which reloads the current page.

User experience suffers from this and it would be desirable if the button would have the same AJAX logic like in the default category product lists or on product pages.

How can we extend the Catalog Products List widget so that it uses the better Add to Cart button? Or is there maybe another workaround?

3

7 Answers 7

25

You have to override addtocart.phtml file

app/design/frontend/vendorname/themename/Magento_Catalog/templates/product/view/addtocart.phtml

Now Just Replace "bindSubmit": false to "bindSubmit": true

<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "catalogAddToCart": {
                "bindSubmit": false
            }
        }
    }
</script>

To

<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "catalogAddToCart": {
                "bindSubmit": true
            }
        }
    }
</script>

Now delete cache and page_cache folder or flush cache.

Note: Make sure you have to set No from Store->Configuration->Sales->Checkout->Shopping Cart->After Adding a Product Redirect to Shopping Cart to No

6
  • This code is not working in magento 2.2.3.
    – Ashna
    Commented May 8, 2018 at 12:55
  • 1
    How we can add ajax add to cart in grid.html for home page widget in 2.2.3
    – Ashna
    Commented May 8, 2018 at 12:56
  • @Prince patel, how this can be re inited? I have installed ajax scroll, so add to cart not working after response Commented Feb 4, 2019 at 13:54
  • @jafarpinjar This is default Magento code. Please check ajax scroll module for the issue. Commented Feb 4, 2019 at 16:06
  • It's working for me after change to this file with this "bindSubmit": true Commented May 9, 2019 at 5:54
7

You can extend the Catalog Products List to use the ajax cart by creating a module with the following files:

/Your/Module/Block/Product/ListBlock.php with content:

<?php

namespace Your\Module\Block\Product;


use Magento\CatalogWidget\Block\Product\ProductsList;

class ListBlock extends ProductsList
{
    /**
     * @var \Magento\Framework\Url\Helper\Data
     */
    protected $urlHelper;

    public function __construct(
        \Magento\Catalog\Block\Product\Context $context,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility,
        \Magento\Framework\App\Http\Context $httpContext,
        \Magento\Rule\Model\Condition\Sql\Builder $sqlBuilder,
        \Magento\CatalogWidget\Model\Rule $rule,
        \Magento\Widget\Helper\Conditions $conditionsHelper,
        \Magento\Framework\Url\Helper\Data $urlHelper,
        array $data
    )
    {
        $this->urlHelper = $urlHelper;

        parent::__construct($context, $productCollectionFactory, $catalogProductVisibility, $httpContext, $sqlBuilder, $rule, $conditionsHelper, $data);
    }

    /**
     * Get post parameters
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return string
     */
    public function getAddToCartPostParams(\Magento\Catalog\Model\Product $product)
    {
        $url = $this->getAddToCartUrl($product);
        return [
            'action' => $url,
            'data' => [
                'product' => $product->getEntityId(),
                \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED =>
                    $this->urlHelper->getEncodedUrl($url),
            ]
        ];
    }
}

Your/Module/etc/widget.xml with content:

<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="ajax_products_list" class="Your\Module\Block\Product\ListBlock" is_email_compatible="true"
            placeholder_image="Magento_CatalogWidget::images/products_list.png" ttl="86400">
        <label translate="true">Ajax Catalog Products List</label>
        <description translate="true">List of Products with Ajax Cart</description>
        <parameters>
            <parameter name="title" xsi:type="text" required="false" visible="true">
                <label translate="true">Title</label>
            </parameter>
            <parameter name="show_pager" xsi:type="select" visible="true"
                       source_model="Magento\Config\Model\Config\Source\Yesno">
                <label translate="true">Display Page Control</label>
            </parameter>
            <parameter name="products_per_page" xsi:type="text" required="true" visible="true">
                <label translate="true">Number of Products per Page</label>
                <depends>
                    <parameter name="show_pager" value="1" />
                </depends>
                <value>5</value>
            </parameter>
            <parameter name="products_count" xsi:type="text" required="true" visible="true">
                <label translate="true">Number of Products to Display</label>
                <value>10</value>
            </parameter>
            <parameter name="template" xsi:type="select" required="true" visible="true">
                <label translate="true">Template</label>
                <options>
                    <option name="default" value="Your_Module::product/widget/content/grid.phtml" selected="true">
                        <label translate="true">Products Grid Template</label>
                    </option>
                </options>
            </parameter>
            <parameter name="cache_lifetime" xsi:type="text" visible="true">
                <label translate="true">Cache Lifetime (Seconds)</label>
                <description translate="true">86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache.</description>
            </parameter>
            <parameter name="condition" xsi:type="conditions" visible="true" required="true" sort_order="10"
                       class="Magento\CatalogWidget\Block\Product\Widget\Conditions">
                <label translate="true">Conditions</label>
            </parameter>
        </parameters>
        <containers>
            <container name="content">
                <template name="grid" value="default" />
            </container>
            <container name="content.top">
                <template name="grid" value="default" />
            </container>
            <container name="content.bottom">
                <template name="grid" value="default" />
            </container>
        </containers>
    </widget>
</widgets>

And finally Your/Module/view/frontend/templates/product/widget/content/grid.phtml with content:

<?php

// @codingStandardsIgnoreFile
use Magento\Framework\App\Action\Action;
?>
<?php
/**
 * Template for displaying products list widget
 *
 * @var $block \Your\Module\Block\Product\ListBlock
 */
?>
<?php if ($exist = ($block->getProductCollection() && $block->getProductCollection()->getSize())):?>
<?php
    $type = 'widget-product-grid';

    $mode = 'grid';

    $image = 'new_products_content_widget_grid';
    $title = $block->getTitle() ? __($block->getTitle()) : '';
    $products = $block->getProductCollection()->getItems();
    $_helper = $this->helper('Magento\Catalog\Helper\Output');

    $showWishlist = true;
    $showCompare = true;
    $showCart = true;
    $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::DEFAULT_VIEW;
    $showDescription = false;
?>
    <div class="block widget block-products-list <?php /* @escapeNotVerified */ echo $mode; ?>">
        <?php if ($title):?>
        <div class="block-title">
            <strong><?php /* @escapeNotVerified */ echo $title; ?></strong>
        </div>
        <?php endif ?>
        <div class="block-content">
            <?php /* @escapeNotVerified */ echo '<!-- ' . $image . '-->' ?>
            <div class="products-<?php /* @escapeNotVerified */ echo $mode; ?> <?php /* @escapeNotVerified */ echo $mode; ?>">
                <ol class="product-items <?php /* @escapeNotVerified */ echo $type; ?>">
                    <?php $iterator = 1; ?>
                    <?php /** @var \Magento\Catalog\Model\Product $_product */ ?>
                    <?php foreach ($products as $_product): ?>
                        <?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="product-item">' : '</li><li class="product-item">' ?>
                        <div class="product-item-info" data-container="product-grid">
                            <?php
                            $productImage = $block->getImage($_product, $image);
                            ?>
                            <?php // Product Image ?>
                            <a href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>" class="product photo product-item-photo" tabindex="-1">
                                <?php echo $productImage->toHtml(); ?>
                            </a>
                            <div class="product details product-item-details">
                                <?php
                                $_productNameStripped = $block->stripTags($_product->getName(), null, true);
                                ?>
                                <strong class="product name product-item-name">
                                    <a class="product-item-link"
                                       href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>">
                                        <?php /* @escapeNotVerified */ echo $_helper->productAttribute($_product, $_product->getName(), 'name'); ?>
                                    </a>
                                </strong>
                                <?php echo $block->getReviewsSummaryHtml($_product, $templateType); ?>
                                <?php /* @escapeNotVerified */ echo $block->getProductPrice($_product) ?>
                                <?php echo $block->getProductDetailsHtml($_product); ?>

                                <div class="product-item-inner">
                                    <div class="product actions product-item-actions">
                                        <div class="actions-primary">
                                            <?php if ($_product->isSaleable()): ?>
                                                <?php $postParams = $block->getAddToCartPostParams($_product); ?>
                                                <form data-role="tocart-form" action="<?php /* @escapeNotVerified */ echo $postParams['action']; ?>" method="post">
                                                    <input type="hidden" name="product" value="<?php /* @escapeNotVerified */ echo $postParams['data']['product']; ?>">
                                                    <input type="hidden" name="<?php /* @escapeNotVerified */ echo Action::PARAM_NAME_URL_ENCODED; ?>" value="<?php /* @escapeNotVerified */ echo $postParams['data'][Action::PARAM_NAME_URL_ENCODED]; ?>">
                                                    <?php echo $block->getBlockHtml('formkey')?>
                                                    <button type="submit"
                                                            title="<?php echo $block->escapeHtml(__('Add to Cart')); ?>"
                                                            class="action tocart primary">
                                                        <span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>
                                                    </button>
                                                </form>
                                            <?php else: ?>
                                                <?php if ($_product->getIsSalable()): ?>
                                                    <div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div>
                                                <?php else: ?>
                                                    <div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div>
                                                <?php endif; ?>
                                            <?php endif; ?>
                                        </div>
                                        <div data-role="add-to-links" class="actions-secondary">
                                            <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow()): ?>
                                                <a href="#"
                                                   class="action towishlist"
                                                   title="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"
                                                   aria-label="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"
                                                   data-post='<?php /* @escapeNotVerified */ echo $block->getAddToWishlistParams($_product); ?>'
                                                   data-action="add-to-wishlist"
                                                   role="button">
                                                    <span><?php /* @escapeNotVerified */ echo __('Add to Wish List') ?></span>
                                                </a>
                                            <?php endif; ?>
                                            <?php
                                            $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');
                                            ?>
                                            <a href="#"
                                               class="action tocompare"
                                               title="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"
                                               aria-label="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"
                                               data-post='<?php /* @escapeNotVerified */ echo $compareHelper->getPostDataParams($_product); ?>'
                                               role="button">
                                                <span><?php /* @escapeNotVerified */ echo __('Add to Compare') ?></span>
                                            </a>
                                        </div>
                                    </div>
                                    <?php if ($showDescription):?>
                                        <div class="product description product-item-description">
                                            <?php /* @escapeNotVerified */ echo $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?>
                                            <a href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>" title="<?php /* @escapeNotVerified */ echo $_productNameStripped ?>"
                                               class="action more"><?php /* @escapeNotVerified */ echo __('Learn More') ?></a>
                                        </div>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </div>
                        <?php echo($iterator == count($products)+1) ? '</li>' : '' ?>
                    <?php endforeach ?>
                </ol>
            </div>
            <?php echo $block->getPagerHtml() ?>
        </div>
    </div>
    <?php if (!$block->isRedirectToCartEnabled()) : ?>
        <script type="text/x-magento-init">
        {
            "[data-role=tocart-form], .form.map.checkout": {
                "catalogAddToCart": {}
            }
        }
        </script>
    <?php endif; ?>
<?php endif;?>

Be sure and replace all instances of 'Your\Module' with your own namespaces.

1
  • 1
    Not sure what you changed in the phtml fille. Can you maybe clear things out by saying what you changed and why (the other filles are pretty straight forward) Commented Oct 15, 2017 at 16:38
4

Solution for Magento 2.3

For widget Catalog Product list:

You should find template of widget in this case it is

module-catalog-widget/view/frontend/templates/product/widget/content/grid.phtml

and insert this code:

<script type="text/x-magento-init">
    {
        "[data-role=tocart-form], .form.map.checkout": {
            "catalogAddToCart": {}
        }
    }
</script>
3
  • It works fine. Thanks.
    – Aji
    Commented Jan 4, 2020 at 14:19
  • It's not working in Magento 2.3.4
    – Ashna
    Commented May 27, 2020 at 12:38
  • It works well in 2.3.6, thanks!
    – TheFrakes
    Commented May 12, 2021 at 15:24
2

In Magento 2.2, the Magento_Catalog/product/view/validation script is called instead of catalogAddToCart.

This is already present in addtocart.phtml (with the radioCheckboxClosest config option), so to enable the Ajax Add to Cart just add the bindSubmit option like so:

<script type="text/x-magento-init">
{
    "#product_addtocart_form": {
        "Magento_Catalog/product/view/validation": {
            "bindSubmit": true,
            "radioCheckboxClosest": ".nested"
        }
    }
}
</script>
2
  • Adding this setting does not work for me
    – Muckee
    Commented May 14, 2018 at 21:28
  • Worked flawlessly in v2.2.3, thank you! Commented Sep 3, 2018 at 18:35
1

In the latest Magento 2 releases this was changed again and it is now

<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "Magento_Catalog/js/validate-product": {
                "bindSubmit": true
            }
        }
    }
</script>
0

In Magento 2.3.5 go to the file Magento_CatalogWidget/templates/product/widget/content/grid.phtml where you're overriding the catalog widget template.

Replace

<form data-role="tocart-form" action="<?php echo $postParams['action']; ?>" method="post">

by

<form data-role="tocart-form"
  data-product-sku="<?= $block->escapeHtmlAttr($_item->getSku()) ?>"
  action="<?= $block->escapeUrl($postParams['action']) ?>"
  method="post">
  <input type="hidden" name="product" value="<?= /* @noEscape */ $postParams['data']['product'] ?>">
  <input type="hidden" name="uenc" value="<?= /* @noEscape */ $postParams['data']['uenc'] ?>">
  <?= $block->getBlockHtml('formkey') ?>

Then add the script

<script type="text/x-magento-init">
    {
        "[data-role=tocart-form], .form.map.checkout": {
            "catalogAddToCart": {
                "product_sku": "<?= $block->escapeJs($_item->getSku()) ?>"
            }
        }
    }
</script>

It worked perfectly for me

0

Add method to your class

public function __construct(
    ......................................................
    \Magento\Catalog\Model\ProductFactory $productFactory,
    \Magento\Catalog\Block\Product\ListProduct $listProductBlock,
    ......................................................
) {
    ......................................................
    $this->productFactory = $productFactory;
    $this->listProductBlock = $listProductBlock;
    ......................................................
}

public function getProductData($id)
{
    $collection = $this->productFactory->create()->load($id);
    return $collection;
}

public function getAddToCartPostParams($product)
{
    return $this->listProductBlock->getAddToCartPostParams($product);
}

Add into your phtml file

<?php
$_product = $block->getProductData(11);
?>
<?php $postParams = $block->getAddToCartPostParams($_product); ?>
<form data-role="tocart-form" action="<?php /* @escapeNotVerified */ echo $postParams['action']; ?>" method="post">
    <input type="hidden" name="product" value="<?php /* @escapeNotVerified */ echo $postParams['data']['product']; ?>">
    <input type="hidden" name="<?php /* @escapeNotVerified */ echo "uenc"; ?>" value="<?php /* @escapeNotVerified */ echo $postParams['data']['uenc']; ?>">
    <?php echo $block->getBlockHtml('formkey')?>
    <button type="submit"
            title="<?php echo $block->escapeHtml(__('Add to Cart')); ?>"
            class="action tocart primary">
        <span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>
    </button>
</form>
<script type="text/x-magento-init">
    {
        "[data-role=tocart-form]": {
            "Magento_Catalog/js/validate-product": {
                "bindSubmit": true
            }
        }
    }
</script>

Not the answer you're looking for? Browse other questions tagged or ask your own question.