4

while add to compare on product listing page I want to add a popup for success message instead of Magento 2 default success message.

Help would be appreciated.

4
  • Seem that you want to use Ajax for compare product? Commented May 24, 2017 at 9:19
  • @khoa I want success message on modal popup instead of default Magento success message.
    – rajat kara
    Commented May 24, 2017 at 10:56
  • If I remember exactly, on the product listing page, if adding a product to the compare list, the page will be loaded (not Ajax) and then, the success message will show? Now, you don't want page load and use Ajax without reloading page? Commented May 24, 2017 at 12:02
  • Yes how to do ajax add to compare in Magento 2
    – rajat kara
    Commented May 25, 2017 at 11:12

1 Answer 1

3

I have just finished a module for adding compare products via Ajax. The logic of module just followed the Magento core logic:

  • Controller logic: vendor/magento/module-catalog/Controller/Product/Compare/Add.php
  • Javacript logic: vendor/magento/module-catalog/view/frontend/web/js/catalog-add-to-cart.js

1) Declare our route.xml

app/code/Vendor/CatalogProduct/etc/frontend/routes.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="catalog_product" frontName="catalog_product">
            <module name="Vendor_CatalogProduct" />
        </route>
    </router>
</config>

2) We need to override the Block: \Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare, because we need to build the Ajax url for each product. And, after adding product successfully, we need to reload compare product data (stored in browser storage).

app/code/Vendor/CatalogProduct/etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare" type="Vendor\CatalogProduct\Block\ProductList\Item\AddTo\Compare"/>
</config>

app/code/Vendor/CatalogProduct/etc/frontend/sections.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <action name="catalog_product/compare/ajaxAdd">
        <section name="compare-products"/>
    </action>
</config>

3) Before building the Ajax controller logic and Js script, we need to custom the compare product layout.

app/code/Vendor/CatalogProduct/view/frontend/layout/catalog_category_view.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
                <referenceBlock name="category.product.addto.compare">
                    <action method="setTemplate">
                        <argument name="template" xsi:type="string">Vendor_CatalogProduct::compare/list/addto/compare.phtml</argument>
                    </action>
                </referenceBlock>
            <block class="Vendor\CatalogProduct\Block\Compare\Js" template="Vendor_CatalogProduct::compare/list/js.phtml" name="js.compare" after="-"/>
        </referenceContainer>
    </body>
</page> 

Change the link to form data:

app/code/Vendor/CatalogProduct/view/frontend/templates/compare/list/addto/compare.phtml

<?php

// @codingStandardsIgnoreFile
/** @var $block \Vendor\CatalogProduct\Block\ProductList\Item\AddTo\Compare */
?>

<form data-role="addto-compare-form" action="<?php /* @escapeNotVerified */ echo $block->getAddToCompareAjaxUrl($this->getProduct())?>" method="post">
    <?php echo $block->getBlockHtml('formkey')?>
    <button type="submit" role="button"
            title="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"
            class="action addtocompare primary">
        <span><?php /* @escapeNotVerified */ echo __('Add to Compare') ?></span>
    </button>
</form>

Need to add the Js script to template:

app/code/Vendor/CatalogProduct/view/frontend/templates/compare/list/js.phtml

<script type="text/x-magento-init">
    {
        "[data-role=addto-compare-form]": {
            "Vendor_CatalogProduct/js/add-to-compare": {}
        }
    }
</script>

Blocks:

Vendor\CatalogProduct\Block\Compare\Js should extend from \Magento\Framework\View\Element\Template.

Vendor\CatalogProduct\Block\ProductList\Item\AddTo\Compare will extend from Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare.

public function getAddToCompareAjaxUrl($product) 
{ 
   $productId = $product->getId(); 
   return $this->getUrl('catalog_product/compare/ajaxAdd', ['product' => $productId]); 
}

4) Js script and Ajax controller Logic: followed the Magento core logic.

app/code/Vendor/CatalogProduct/view/frontend/web/js/add-to-compare.js

/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define([
    'jquery',
    'mage/translate',
    'jquery/ui'
], function($, $t) {
    "use strict";

    $.widget('mage.catalogAddToCompare', {

        options: {
            processStart: null,
            processStop: null,
            bindSubmit: true,
            messagesSelector: '[data-placeholder="messages"]'
        },

        _create: function() {
            if (this.options.bindSubmit) {
                this._bindSubmit();
            }
        },

        _bindSubmit: function() {
            var self = this;
            this.element.on('submit', function(e) {
                e.preventDefault();
                self.submitForm($(this));
            });
        },

        isLoaderEnabled: function() {
            return this.options.processStart && this.options.processStop;
        },

        /**
         * Handler for the form 'submit' event
         *
         * @param {Object} form
         */
        submitForm: function (form) {
            var self = this;
            self.ajaxSubmit(form);

        },

        ajaxSubmit: function(form) {
            var self = this;
            $.ajax({
                url: form.attr('action'),
                data: form.serialize(),
                type: 'post',
                dataType: 'json',
                beforeSend: function() {
                    if (self.isLoaderEnabled()) {
                        $('body').trigger(self.options.processStart);
                    }
                },
                success: function(res) {
                    if (self.isLoaderEnabled()) {
                        $('body').trigger(self.options.processStop);
                    }

                    if (res.backUrl) {
                        window.location = res.backUrl;
                        return;
                    }
                    if (res.messages) {
                        //$(self.options.messagesSelector).html(res.messages);
                    }
                }
            });
        }
    });
    return $.mage.catalogAddToCompare;
});

app/code/Vendor/CatalogProduct/Controller/Compare/AjaxAdd.php

<?php

namespace Vendor\CatalogProduct\Controller\Compare;

use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Json\Helper\Data;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Customer\Model\Session as CustomerSession;
use Psr\Log\LoggerInterface;
use Magento\Framework\Data\Form\FormKey\Validator;
use Magento\Customer\Model\Visitor;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Catalog\Model\Product\Compare\ListCompare;

class AjaxAdd extends Action
{
    /**
     * @var CustomerSession
     */
    protected $customerSession;

    /**
     * @var Visitor
     */
    protected $customerVisitor;

    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    protected $catalogProductCompareList;

    /**
     * @var Validator
     */
    protected $formKeyValidator;

    /**
     * @var JsonFactory
     */
    protected $jsonFactory;

    /**
     * @var Data
     */
    protected $jsonHelper;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * AjaxAdd constructor.
     *
     * @param Context $context
     * @param CustomerSession $customerSession
     * @param JsonFactory $jsonFactory
     * @param Data $jsonHelper
     * @param Visitor $visitor
     * @param Validator $validator
     * @param ProductRepositoryInterface $productRepository
     * @param StoreManagerInterface $storeManager
     * @param ListCompare $listCompare
     * @param LoggerInterface $logger
     */
    public function __construct(
        Context $context,
        CustomerSession $customerSession,
        JsonFactory $jsonFactory,
        Data $jsonHelper,
        Visitor $visitor,
        Validator $validator,
        ProductRepositoryInterface $productRepository,
        StoreManagerInterface $storeManager,
        ListCompare $listCompare,
        LoggerInterface $logger
    ) {
        $this->customerSession = $customerSession;
        $this->jsonFactory = $jsonFactory;
        $this->jsonHelper = $jsonHelper;
        $this->customerVisitor = $visitor;
        $this->formKeyValidator = $validator;
        $this->productRepository = $productRepository;
        $this->storeManager = $storeManager;
        $this->catalogProductCompareList = $listCompare;
        $this->logger = $logger;
        parent::__construct($context);
    }

    /**
     * Ajax execute
     *
     */
    public function execute()
    {
        $response = [
            'errors' => false
        ];
        if ($this->getRequest()->isAjax()) {
            if (!$this->formKeyValidator->validate($this->getRequest())) {
                /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */
                $this->messageManager->addErrorMessage(__('Invalid form key.'));
                $response = [
                    'errors' => true,
                    'messages' => __('Invalid form key.')
                ];
                $resultJson = $this->jsonFactory->create();
                return $resultJson->setData($response);
            }

            try {
                $productId = (int)$this->getRequest()->getParam('product');
                if ($productId && ($this->customerVisitor->getId() || $this->customerSession->isLoggedIn())) {
                    $storeId = $this->storeManager->getStore()->getId();
                    try {
                        $product = $this->productRepository->getById($productId, false, $storeId);
                    } catch (NoSuchEntityException $e) {
                        $product = null;
                    }

                    if ($product) {
                        $this->catalogProductCompareList->addProduct($product);
                        $productName = $this->_objectManager->get('Magento\Framework\Escaper')->escapeHtml($product->getName());
                        $response['messages'] = __('You added product %1 to the comparison list.', $productName);
                        $this->messageManager->addSuccessMessage(__('You added product %1 to the comparison list.', $productName));
                        $this->_eventManager->dispatch('catalog_product_compare_add_product', ['product' => $product]);
                    }

                    $this->_objectManager->get('Magento\Catalog\Helper\Product\Compare')->calculate();
                }

            } catch (\Exception $e) {
                $response = [
                    'errors' => true,
                    'messages' => __('Some thing went wrong.')
                ];
                $this->messageManager->addExceptionMessage($e, __('Some thing went wrong.'));
                $this->logger->critical($e);
            }
        } else {
            $response = [
                'errors' => true,
                'messages' => __('Need to access via Ajax.')
            ];
            $this->messageManager->addErrorMessage(__('Need to access via Ajax.'));
        }
        /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */
        $resultJson = $this->jsonFactory->create();
        return $resultJson->setData($response);
    }
}

Remember to define registration.php and module.xml.

Now, on the product listing page, we can use Ajax for compare product function. However, this module only works on Product listing page, we need to improve it, it should work on Product detail page.

8
  • Hello sire good day, I appreciate your answer but I just wanna ask where is the code for your Vendor\CatalogProduct\Block\ProductList\Item\AddTo\Compare ? Commented Oct 16, 2018 at 6:32
  • and also sir for the Vendor\CatalogProduct\Block\Compare\Js Commented Oct 16, 2018 at 7:09
  • hi @YubiA. let me check again. Seems that I forgot something. Commented Oct 16, 2018 at 8:31
  • Vendor\CatalogProduct\Block\Compare\Js should extend from \Magento\Framework\View\Element\Template. Commented Oct 16, 2018 at 8:41
  • That's what exactly I did sir. but for the Vendor\CatalogProduct\Block\ProductList\Item\AddTo\Compare I am missing the implemetation for the getAddToCompareAjaxUrl() method Commented Oct 16, 2018 at 8:44

Your Answer

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

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