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.

  • Seem that you want to use Ajax for compare product? – Khoa TruongDinh May 24 '17 at 9:19
  • @khoa I want success message on modal popup instead of default Magento success message. – rajat kara May 24 '17 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? – Khoa TruongDinh May 24 '17 at 12:02
  • Yes how to do ajax add to compare in Magento 2 – rajat kara May 25 '17 at 11:12
2

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.

  • 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 ? – Eubie Aluad Oct 16 '18 at 6:32
  • and also sir for the Vendor\CatalogProduct\Block\Compare\Js – Eubie Aluad Oct 16 '18 at 7:09
  • hi @YubiA. let me check again. Seems that I forgot something. – Khoa TruongDinh Oct 16 '18 at 8:31
  • Vendor\CatalogProduct\Block\Compare\Js should extend from \Magento\Framework\View\Element\Template. – Khoa TruongDinh Oct 16 '18 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 – Eubie Aluad Oct 16 '18 at 8:44

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.