Magento – Custom popup message while add to compare on product list page

comparelistingmagento2success-message

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.

Best Answer

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.

Related Topic