12

I am trying to find good solution to execute custom javascript when product is successfully added to cart. Also this custom javascript need information about product that was added to cart (sku, qty, price, name, etc.)

Currently I come up with the following way to get information about products added to cart:

  1. Rewrite $.mage.catalogAddToCart.prototype.enableAddToCartButton (for products added to cart from catalog or product view page)
  2. Rewrite $.mage.dataPost.prototype.postData (for products added to cart from widgets)

To get necessary information I have to parse page (f.e. to get qty) and output additional infromation to page (f.e. to get sku having product id)

However my solution:

  • has two entry points
  • does not look nice
  • does not handle situation when validation is failed on backend
  • does not provide me all required information conveniently

Unfortunately I was not able to find any suitable extension point of checkout minicart to resolve my problem.

Any suggestions are much appreciated!

3
  • I have a similar situation. I have reviewed Magento code and logic and does not find any solution. So, Magento does not support it and maybe, it makes sense because you can add product without Ajax request. But, I must have solution for my task and I wrote the following file (cart-update.js): require([ 'jquery', 'underscore', 'jquery/jquery-storageapi' ], function($, _) { var storage = $.initNamespaceStorage('mage-cache-storage').localStorage; $(document).on('ajaxComplete', function (event, xhr, settings) { if (settings.url.match(/customer\/section\/load/i) && xhr.responseJSON && xhr.responseJ
    – Oleksiy
    Aug 16, 2017 at 20:54
  • Have you tried using mixins? alanstorm.com/the-curious-case-of-magento-2-mixins I personally use them often. Just like the php version of plugins. If you get stuck let me know and I can provide a solution. Jul 25, 2018 at 17:21
  • @ChrisAnderson thanks, but actually my current ugly solution described in the question is implemented using mixins. Jul 27, 2018 at 11:28

2 Answers 2

10

When something is added to the cart, the local storage information is updated and you can subscribe to a knockout observable in the cart section of the customer data to be notified when it's content changes.

Here is an example:

require(['Magento_Customer/js/customer-data'], function (customerData) {

    var cart = customerData.get('cart');
    var count = cart().summary_count;
    cart.subscribe(function () {
        if (cart().summary_count !== count) {
            count = cart().summary_count;
            // do something here
            console.log('Number of items in cart is now: ' + count);
        }
    });
});
4
  • Even with a "domReady!" in the require block, seems to run too early on as cart().summary_count always 'undefined' ?
    – 00-BBB
    May 11, 2020 at 14:27
  • That is why you subscribe to the cart
    – Quinten
    May 12, 2020 at 15:14
  • This worked for me on Magento 2.3.4. count variable updates after adding to the cart
    – Jiyaad.D
    Jun 3, 2020 at 10:14
  • Works like a charm in Magento 2.2.2 Jun 4, 2020 at 15:28
1

A solution is to write a mixin for the catalogAddToCart ajaxSubmit function. Usually the data you require would not be available here but this can be fixed by modifying the checkout/cart/add controller to return the data. Full solution below, currently it just javascript alerts the data you needed on add to cart success.

app/code/VendorName/ModuleName/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'VendorName_ModuleName',
    __DIR__
);

app/code/VendorName/ModuleName/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="VendorName_ModuleName" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Catalog"/>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

app/code/VendorName/ModuleName/etc/frontend/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Checkout\Controller\Cart\Add" type="VendorName\ModuleName\Controller\Cart\Add"/>
</config>

app/code/VendorName/ModuleName/Controller/Cart/Add.php

<?php

namespace VendorName\ModuleName\Controller\Cart;

/**
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class Add extends \Magento\Checkout\Controller\Cart\Add
{
    /**
     * Add product to shopping cart action
     *
     * @return \Magento\Framework\Controller\Result\Redirect
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     */
    public function execute()
    {
        if (!$this->_formKeyValidator->validate($this->getRequest())) {
            return $this->resultRedirectFactory->create()->setPath('*/*/');
        }

        $params = $this->getRequest()->getParams();

        try {
            if (isset($params['qty'])) {
                $filter = new \Zend_Filter_LocalizedToNormalized(
                    ['locale' => $this->_objectManager->get(
                        \Magento\Framework\Locale\ResolverInterface::class
                    )->getLocale()]
                );
                $params['qty'] = $filter->filter($params['qty']);
            }

            $product = $this->_initProduct();
            $related = $this->getRequest()->getParam('related_product');

            /**
             * Check product availability
             */
            if (!$product) {
                return $this->goBack();
            }

            $this->cart->addProduct($product, $params);
            if (!empty($related)) {
                $this->cart->addProductsByIds(explode(',', $related));
            }

            $this->cart->save();

            /**
             * @todo remove wishlist observer \Magento\Wishlist\Observer\AddToCart
             */
            $this->_eventManager->dispatch(
                'checkout_cart_add_product_complete',
                ['product' => $product, 'request' => $this->getRequest(), 'response' => $this->getResponse()]
            );

            if (!$this->_checkoutSession->getNoCartRedirect(true)) {
                if (!$this->cart->getQuote()->getHasError()) {
                    $message = __(
                        'You added %1 to your shopping cart.',
                        $product->getName()
                    );
                    $this->messageManager->addSuccessMessage($message);
                }
                return $this->goBack(null, $product, [
                    'sku' => $product->getSku(),
                    'name' => $product->getName(),
                    'price' => $product->getPrice(),
                    'qty' => isset($params['qty'])?$params['qty']:null
                ]);
            }
        } catch (\Magento\Framework\Exception\LocalizedException $e) {
            if ($this->_checkoutSession->getUseNotice(true)) {
                $this->messageManager->addNotice(
                    $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($e->getMessage())
                );
            } else {
                $messages = array_unique(explode("\n", $e->getMessage()));
                foreach ($messages as $message) {
                    $this->messageManager->addError(
                        $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($message)
                    );
                }
            }

            $url = $this->_checkoutSession->getRedirectUrl(true);

            if (!$url) {
                $cartUrl = $this->_objectManager->get(\Magento\Checkout\Helper\Cart::class)->getCartUrl();
                $url = $this->_redirect->getRedirectUrl($cartUrl);
            }

            return $this->goBack($url);
        } catch (\Exception $e) {
            $this->messageManager->addException($e, __('We can\'t add this item to your shopping cart right now.'));
            $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e);
            return $this->goBack();
        }
    }

    /**
     * Resolve response
     *
     * @param string $backUrl
     * @param \Magento\Catalog\Model\Product $product
     * @return $this|\Magento\Framework\Controller\Result\Redirect
     */
    protected function goBack($backUrl = null, $product = null, $result = [])
    {
        if (!$this->getRequest()->isAjax()) {
            return parent::_goBack($backUrl);
        }

        if ($backUrl || $backUrl = $this->getBackUrl()) {
            $result['backUrl'] = $backUrl;
        } else {
            if ($product && !$product->getIsSalable()) {
                $result['product'] = [
                    'statusText' => __('Out of stock')
                ];
            }
        }

        $this->getResponse()->representJson(
            $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($result)
        );
    }
}

app/code/VendorName/ModuleName/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_Catalog/js/catalog-add-to-cart': {
                'VendorName_ModuleName/js/catalog-add-to-cart-mixin': true
            },
        }
    }
};

app/code/VendorName/ModuleName/view/frontend/web/js/catalog-add-to-cart-mixin.js

define([
    'jquery',
], function ($) {
    'use strict';

    return function (widget) {
        $.widget('mage.catalogAddToCart', widget, {
            /**
             * @param {String} form
             */
            ajaxSubmit: function (form) {
                var self = this;

                $(self.options.minicartSelector).trigger('contentLoading');
                self.disableAddToCartButton(form);

                $.ajax({
                    url: form.attr('action'),
                    data: form.serialize(),
                    type: 'post',
                    dataType: 'json',

                    /** @inheritdoc */
                    beforeSend: function () {
                        if (self.isLoaderEnabled()) {
                            $('body').trigger(self.options.processStart);
                        }
                    },

                    /** @inheritdoc */
                    success: function (res) {
                        alert(JSON.stringify(res));
                        var eventData, parameters;

                        $(document).trigger('ajax:addToCart', form.data().productSku);

                        if (self.isLoaderEnabled()) {
                            $('body').trigger(self.options.processStop);
                        }

                        if (res.backUrl) {
                            eventData = {
                                'form': form,
                                'redirectParameters': []
                            };
                            // trigger global event, so other modules will be able add parameters to redirect url
                            $('body').trigger('catalogCategoryAddToCartRedirect', eventData);

                            if (eventData.redirectParameters.length > 0) {
                                parameters = res.backUrl.split('#');
                                parameters.push(eventData.redirectParameters.join('&'));
                                res.backUrl = parameters.join('#');
                            }
                            window.location = res.backUrl;

                            return;
                        }

                        if (res.messages) {
                            $(self.options.messagesSelector).html(res.messages);
                        }

                        if (res.minicart) {
                            $(self.options.minicartSelector).replaceWith(res.minicart);
                            $(self.options.minicartSelector).trigger('contentUpdated');
                        }

                        if (res.product && res.product.statusText) {
                            $(self.options.productStatusSelector)
                                .removeClass('available')
                                .addClass('unavailable')
                                .find('span')
                                .html(res.product.statusText);
                        }
                        self.enableAddToCartButton(form);
                    }
                });
            }
        });

        return $.mage.catalogAddToCart;
    }
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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