Magento2 | PWA | GraphQL

Add New Step in checkout with Product widget Magento2


In this post we wiil check how to add new step in checkout in Magento2. In this new step we will add Product widget as a content and display some products.

Let's start by creating custom module.

You can find complete module on Github at Magelearn_NewStepCheckout





Create folder inside app/code/Magelearn/NewStepCheckout

Add registration.php file in it:

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magelearn_NewStepCheckout', __DIR__);

Add composer.json file in it:

{
    "name": "magelearn/module-new-step-checkout",
    "description": "Add New Step in checkout with Product widget Magento2",
    "type": "magento2-module",
    "require": {},
    "authors": [
        {
            "name": "vijay rami",
            "email": "vijaymrami@gmail.com"
        }
    ],
    "license": "proprietary",
    "minimum-stability": "dev",
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Magelearn\\NewStepCheckout\\": ""
        }
    }
}

Add etc/module.xml file in it:

<?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="Magelearn_NewStepCheckout">
        <sequence>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

Now to add new step in checkout first we will modify checkout_index_index.xml file.

Add app/code/Magelearn/NewStepCheckout/view/frontend/layout/checkout_index_index.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="new-step-products" xsi:type="array">
                                            <item name="component" xsi:type="string">Magelearn_NewStepCheckout/js/view/new-step</item>
                                            <item name="sortOrder" xsi:type="string">0</item>
                                            <!--To display step content before shipping step "sortOrder" value should be < 1-->
                                            <!--To display step content between shipping step and payment step  1 < "sortOrder" < 2 -->
                                            <!--To display step content after payment step "sortOrder" > 2 -->
                                            <item name="children" xsi:type="array">
                                                <!--add here child component declaration for your step-->
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Now as per defined in checkout_index_index.xml file, we will add our JS component file.

Add app/code/Magelearn/NewStepCheckout/view/frontend/web/js/view/new-step.js

define([
    'ko',
    'jquery',
    'uiComponent',
    'underscore',
    'Magento_Checkout/js/model/step-navigator',
    'mage/translate',
    'Magento_Checkout/js/model/quote',
    'Magento_Checkout/js/model/cart/totals-processor/default'
], function (ko, $, Component, _, stepNavigator, $t, quote, totalsDefaultProvider) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Magelearn_NewStepCheckout/new-step'
        },

        isVisible: ko.observable(true),
        quoteIsVirtual: quote.isVirtual(),

        /**
         * @returns {*}
         */
        initialize: function () {
            this._super();

            stepNavigator.registerStep(
            	// step code will be used as step content id in the component template
                'new_step',
                // step alias
                null,
                // step title value
                $t('Checkout Products'),
                // observable property with logic when display step or hide step
                this.isVisible,

                _.bind(this.navigate, this),
                /**
                 * sort order value
                 * 'sort order value' < 10: step displays before shipping step;
                 * 10 < 'sort order value' < 20 : step displays between shipping and payment step
                 * 'sort order value' > 20 : step displays after payment step
                 */
                1
            );

            return this;
        },

        navigate: function () {
            this.isVisible(true);
        },

        /**
         * @returns void
         */
        navigateToNextStep: function () {
            stepNavigator.next();
        },

        /**
         * Estimate totals
         */
        updateOrder: function () {
            totalsDefaultProvider.estimateTotals(quote.shippingAddress());
        }
    });
});

If your new step is the first step, you have to create mixins for the payment and shipping steps.

Otherwise, two steps will be activated on the loading of the checkout.

Now we will add app/code/Magelearn/NewStepCheckout/view/frontend/requirejs-config.js file and add slick JS to display products in slider on checkout page and will add shipping-payment-mixin to display this new step at first.

var config = {
	paths: {            
    	'slick': "Magelearn_NewStepCheckout/js/slick"
    },
    'config': {
        'mixins': {
            'Magento_Checkout/js/view/shipping': {
                'Magelearn_NewStepCheckout/js/view/shipping-payment-mixin': true
            },
            'Magento_Checkout/js/view/payment': {
                'Magelearn_NewStepCheckout/js/view/shipping-payment-mixin': true
            }
        }
    },
    shim: {
		'slick': {
	    	deps: ['jquery']
	    }
	}
}

As per defined in above file we will add app/code/Magelearn/NewStepCheckout/view/frontend/web/js/view/shipping-payment-mixin.js

/*
 * If your new step is the first step,
 you have to create mixins for the payment and shipping steps.
 Otherwise, two steps will be activated on the loading of the checkout.
 */
define([
    'ko'
], function (ko) {
    'use strict';

    var mixin = {

        initialize: function () {
            // set visible to be initially false to have additional step show first
            this.visible = ko.observable(false);
            this._super();

            return this;
        }
    };

    return function (target) {
        return target.extend(mixin);
    };
});

We will also add js file for slick slider at app/code/Magelearn/NewStepCheckout/view/frontend/web/js/slick.js

And css file at app/code/Magelearn/NewStepCheckout/view/frontend/web/css/source/_module.less

Now we will add our template file at app/code/Magelearn/NewStepCheckout/view/frontend/web/template/new-step.html

<!--The 'step_code' value from the .js file should be used-->
<li id="new_step" data-bind="fadeVisible: isVisible">
    <div class="step-title" data-bind="i18n: 'You May Also Like'" data-role="title"></div>
    <div id="checkout-step-title" class="step-content" data-role="content">
        <div class="step-products" data-bind="html: window.checkoutConfig.checkout_products_block_content"></div>
        <form data-bind="submit: navigateToNextStep" novalidate="novalidate">
            <div class="actions-toolbar">
                <div class="primary">
                    <button data-role="opc-continue" type="submit" class="button action continue primary">
                        <span><!-- ko i18n: 'Next'--><!-- /ko --></span>
                    </button>
                </div>
            </div>
        </form>
    </div>
</li>

As per highlighted code above, we will add checkout_products_block_content in window.checkoutConfig

For that we will add app/code/Magelearn/NewStepCheckout/etc/frontend/di.xml file.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Model\CompositeConfigProvider">
        <arguments>
            <argument name="configProviders" xsi:type="array">
                <item name="cms_block_config_provider" xsi:type="object">Magelearn\NewStepCheckout\Model\ConfigProvider</item>
            </argument>
        </arguments>
    </type>
</config>

Now add app/code/Magelearn/NewStepCheckout/Model/ConfigProvider.php

<?php

namespace Magelearn\NewStepCheckout\Model;

use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\View\LayoutInterface;

class ConfigProvider implements ConfigProviderInterface
{
    /** @var LayoutInterface  */
    protected $_layout;

    public function __construct(LayoutInterface $layout)
    {
        $this->_layout = $layout;
    }

    public function getConfig()
    {
        $checkoutBlockId = "checkout_products_block"; // CMS Block Identifier

        return [
            'checkout_products_block_content'=> $this->_layout->createBlock('Magento\Cms\Block\Block')->setBlockId($checkoutBlockId)->toHtml()
        ];
    }
}

Now as per image attached in this article we will create a CMS Block with Identifier "checkout_products_block" and will Insert Product List widget with template option "Checkout Products Grid Template".

For that we will add app/code/Magelearn/NewStepCheckout/etc/widget.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="products_list" class="Magento\CatalogWidget\Block\Product\ProductsList">
        <label translate="true">Catalog Products List</label>
        <description translate="true">List of Products</description>
        <parameters>
            <parameter name="template" xsi:type="select" required="true" visible="true">
                <options>
                    <option name="checkoutproducts" value="Magelearn_NewStepCheckout::product/widget/content/grid.phtml">
                        <label translate="true">Checkout Products Grid Template</label>
                    </option>
                </options>
            </parameter>
        </parameters>
        <containers>
            <container name="content">
                <template name="checkoutproducts" value="checkoutproducts"/>
            </container>
            <container name="content.top">
                <template name="checkoutproducts" value="checkoutproducts"/>
            </container>
            <container name="content.bottom">
                <template name="checkoutproducts" value="checkoutproducts"/>
            </container>
        </containers>
    </widget>
</widgets>

Now as per highlighted code above we will add app/code/Magelearn/NewStepCheckout/view/frontend/templates/product/widget/content/grid.phtml file.

We can take the referance from vendor/magento/module-catalog-widget/view/frontend/templates/product/widget/content/grid.phtml file and will modify our file.

<?php

use Magento\Framework\App\Action\Action;

/** @var \Magento\CatalogWidget\Block\Product\ProductsList $block */

// phpcs:disable Generic.Files.LineLength.TooLong
// phpcs:disable Magento2.Templates.ThisInTemplate.FoundHelper
?>
<?php if ($exist = ($block->getProductCollection() && $block->getProductCollection()->getSize())): ?>
    <?php
    $type = 'widget-product-grid';
    $mode = 'grid';
    $image = 'new_products_content_widget_grid';
    $items = $block->getProductCollection()->getItems();
    $showWishlist = false;
    $showCompare = false;
    $showCart = true;
    $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW;
    $description = false;
    ?>
    <div class="block widget block-products-list <?= /* @noEscape */ $mode ?>">
        <?php if ($block->getTitle()): ?>
            <div class="block-title">
                <strong><?= $block->escapeHtml(__($block->getTitle())) ?></strong>
            </div>
        <?php endif ?>
        <div class="block-content">
            <?= /* @noEscape */ '<!-- ' . $image . '-->' ?>
            <div class="custom-checkout-product-slider products-<?= /* @noEscape */ $mode ?> <?= /* @noEscape */ $mode ?>">
                    <?php foreach ($items as $_item): ?>
                        <div class="item">
                            <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-photo">
                                <?= $block->getImage($_item, $image)->toHtml() ?>
                            </a>
                            <div class="product-item-details">
                                <strong class="product-item-name">
                                    <a title="<?= $block->escapeHtml($_item->getName()) ?>"
                                       href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"
                                       class="product-item-link">
                                        <?= $block->escapeHtml($_item->getName()) ?>
                                    </a>
                                </strong>
                                <?php if ($templateType): ?>
                                    <?= $block->getReviewsSummaryHtml($_item, $templateType) ?>
                                <?php endif; ?>

                                <?= $block->getProductPriceHtml($_item, $type) ?>

                                <?= $block->getProductDetailsHtml($_item) ?>

                                <?php if ($showWishlist || $showCompare || $showCart): ?>
                                    <div class="product-item-inner-checkout">
                                        <div class="product-item-actions">
                                            <?php if ($showCart): ?>
                                                <div class="actions-primary">
                                                    <?php if ($_item->isSaleable()): ?>
                                                        <?php $postParams = $block->getAddToCartPostParams($_item); ?>
                                                        <form data-role="tocart-form" data-product-sku="<?= $block->escapeHtml($_item->getSku()) ?>" action="<?= $block->escapeUrl($postParams['action']) ?>" method="post">
                                                            <input type="hidden" name="product" value="<?= $block->escapeHtmlAttr($postParams['data']['product']) ?>">
                                                            <input type="hidden" name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>" value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>">
                                                            <?= $block->getBlockHtml('formkey') ?>
                                                            <button type="submit"
                                                                    title="<?= $block->escapeHtml(__('Add to Cart')) ?>"
                                                                    class="action tocart primary">
                                                                <span><?= $block->escapeHtml(__('Add to Cart')) ?></span>
                                                            </button>
                                                        </form>
                                                    <?php else: ?>
                                                        <?php if ($_item->isAvailable()): ?>
                                                            <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div>
                                                        <?php else: ?>
                                                            <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div>
                                                        <?php endif; ?>
                                                    <?php endif; ?>
                                                </div>
                                            <?php endif; ?>
                                            <?php if ($showWishlist || $showCompare): ?>
                                                <div class="actions-secondary" data-role="add-to-links">
                                                    <?php if ($this->helper(\Magento\Wishlist\Helper\Data::class)->isAllow() && $showWishlist): ?>
                                                        <a href="#"
                                                           data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($_item) ?>' class="action towishlist" data-action="add-to-wishlist" title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>">
                                                            <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span>
                                                        </a>
                                                    <?php endif; ?>
                                                    <?php if ($block->getAddToCompareUrl() && $showCompare): ?>
                                                        <?php $compareHelper = $this->helper(\Magento\Catalog\Helper\Product\Compare::class);?>
                                                        <a href="#" class="action tocompare" data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($_item) ?>' title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>">
                                                            <span><?= $block->escapeHtml(__('Add to Compare')) ?></span>
                                                        </a>
                                                    <?php endif; ?>
                                                </div>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>
                    <?php endforeach ?>
            </div>
            <?= $block->getPagerHtml() ?>
        </div>
    </div>
    <?php if($block->getBlockHtml('formkey')): ?>
    <script type="text/x-magento-init">
    {
        ".block.widget [data-role=tocart-form]": {
            "Magento_Catalog/js/validate-product": {}
        }
    }
    </script>
    <?php endif;?>
<?php endif;?>
<script>
 require(["jquery","slick"],function($,slick){
     $(document).ready(function(){
     	$('.custom-checkout-product-slider').slick({
            slidesToShow: 3,
            slidesToScroll: 1,
            dots: true,
            arrows: true,
            centerMode: true,
            centerPadding: 0,
            focusOnSelect: true,
            responsive: [
                {
                    breakpoint: 767,
                    settings: {
                        slidesToShow: 3,
                    }
                },
                {
                    breakpoint: 560,
                    settings: {
                        slidesToShow: 3,
                    }
                },
                {
                    breakpoint: 480,
                    settings: {
                        slidesToShow: 2,
                    }
                }
            ]
        });
     })
 })
</script>

0 Comments On "Add New Step in checkout with Product widget Magento2"

Back To Top