11

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 '17 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. – Chris Anderson Jul 25 '18 at 17:21
  • @ChrisAnderson thanks, but actually my current ugly solution described in the question is implemented using mixins. – Sergii Ivashchenko Jul 27 '18 at 11:28
6

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 '20 at 14:27
  • That is why you subscribe to the cart – Quinten May 12 '20 at 15:14
  • This worked for me on Magento 2.3.4. count variable updates after adding to the cart – Jiyaad.D Jun 3 '20 at 10:14
  • Works like a charm in Magento 2.2.2 – Shankar Thyagarajan Jun 4 '20 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, privacy policy and cookie policy

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