Magento2 | PWA | GraphQL

Display coupon code, Upsell Products and totals information in Minicart Magneto2


In this post we will check how to display coupon code, Up sell products data (Up sell products of added cart items [simple product's up sell items] ) and totals information in Minicart of Magento2.

Here we have also added code to auto open minicart after product added into cart and +/- qty increment decrement buttons on product details page.

You can find complete module on Github at Magelearn_AdvanceCart








Let's start it by creating custom module.

Create folder inside app/code/Magelearn/AdvanceCart

Add registration.php file in it:
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Magelearn_AdvanceCart',
    __DIR__
);

Add composer.json file in it:

{
    "name": "magelearn/advance-cart",
    "description": "Display coupon code, Up sell products and total information in Minicart magento2",
    "type": "magento2-module",
    "license": "proprietary",
    "version": "1.0.0",
    "authors": [
        {
            "name": "Vijay Rami",
            "email": "vijaymrami@gmail.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Magelearn\\AdvanceCart\\": ""
        }
    }
}

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_AdvanceCart" setup_version="1.0.0"/>
</config>
First we will provide some store configurations options to diplay such a information or not.

Add app/code/Magelearn/AdvanceCart/etc/adminhtml/system.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
   <system>
      <tab id="advancecart" translate="label" sortOrder="10" class="advancecart-tab">
         <label>Advance Cart</label>
      </tab>
      <section id="advancecart_setting" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
         <label>Settings</label>
         <tab>advancecart</tab>
         <resource>Magelearn_AdvanceCart::magelearn_advancecart</resource>
         <group id="minicart_settings" translate="label"  sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
            <label>Minicart Settings</label>
            <field id="cart_text" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Promotional Text</label>
            </field>
			<field id="upsell_title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Upsell Title</label>
            </field>
			<field id="upsell_type" translate="label" sortOrder="3" type="select" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Upsell type</label>
               <!-- source model which we created for drop down options -->
               <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upselltype</source_model>
			   <comment>Option Dynamic will show the products from the list of upsell products of the products added to cart. Option Static shows the products from the Sku entered in option 'Product Sku'.</comment>
            </field>
            <field id="upsell_limit" translate="label" sortOrder="4" type="select" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Upsell Product Limit</label>
               <!-- source model which we created for drop down options -->
               <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upselllimit</source_model>
			   <comment>Set the number for products to show on upsell block.</comment>
            </field>
            <field id="upsell_view" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Upsell View</label>
               <!-- source model which we created for drop down options -->
               <source_model>Magelearn\AdvanceCart\Model\Config\Source\Upsellview</source_model>
			   <comment>Option `Slider` will show the products in a slick slider view. Option `Scroll` shows the products in a scrolling view.</comment>
            </field>
            <field id="totals" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Show total summary</label>
                <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
            </field>
            <field id="totals_title" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Totals summary text</label>
                <depends>
                    <field id="totals">1</field>
                </depends>
            </field>
            <field id="coupon" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Allow user to apply coupon</label>
                <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
            </field>
			<field id="upsell_sku" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0">
               <label>Product Sku</label>
			   <depends>
					<field id="advancecart_setting/minicart_settings/upsell_type">2</field>
				</depends>
            </field>
			
		</group>
      </section>
   </system>
</config>

As per highlighted code above we will add source_model files to provide proper system configurations options.

Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upselllimit.php file.

<?php
namespace Magelearn\AdvanceCart\Model\Config\Source;

class Upselllimit implements \Magento\Framework\Option\ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        $options = [];

        foreach (range(1, 10) as $number) {
		    $options[] = ['value' => $number, 'label' => __($number)];
		}
        return $options;
    }
}

Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upselltype.php file.

<?php
namespace Magelearn\AdvanceCart\Model\Config\Source;

class Upselltype implements \Magento\Framework\Option\ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        return [
            ['value' => '1', 'label' => __('Dynamic')],
            ['value' => '2', 'label' => __('Static')]
        ];
    }
}

Create app/code/Magelearn/AdvanceCart/Model/Config/Source/Upsellview.php file.

<?php
namespace Magelearn\AdvanceCart\Model\Config\Source;

class Upsellview implements \Magento\Framework\Option\ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        return [
            ['value' => '1', 'label' => __('Slider View')],
            ['value' => '2', 'label' => __('Scroll View')]
        ];
    }
}

And will set these options default values in etc/config.xml file.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <advancecart_setting>
            <minicart_settings>
				<cart_text>Above $100 to qualify for free shipping!</cart_text>
				<coupon>1</coupon>
				<totals>1</totals>
				<totals_title>Your Total:</totals_title>
                <upsell_title>Don't forget.</upsell_title>
				<upsell_type>1</upsell_type>
				<upsell_limit>3</upsell_limit>
				<upsell_view>2</upsell_view>
            </minicart_settings>
        </advancecart_setting>
    </default>
</config>
First we will check how to add "Add to Cart" button on catalog product details page.
For that we will add xml file at app/code/Magelearn/AdvanceCart/view/frontend/layout/catalog_product_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>
        <referenceBlock name="product.info.addtocart.additional">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Magelearn_AdvanceCart::product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
        <referenceBlock name="product.info.addtocart">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Magelearn_AdvanceCart::product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
    </body>
</page>
Now add template file accordingly.
Add app/code/Magelearn/AdvanceCart/view/frontend/templates/product/view/addtocart.phtml file.
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/** @var $block \Magento\Catalog\Block\Product\View */
?>
<?php $_product = $block->getProduct(); ?>
<?php $buttonTitle = __('Add to Cart'); ?>
<?php if ($_product->isSaleable()) :?>
<div class="box-tocart">
    <div class="fieldset">
        <?php if ($block->shouldRenderQuantity()) :?>
        <div class="field qty">
            <label class="label" for="qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></label>
            <div class="control">
            	<span class="minus"><button type="button" title="<?= $block->escapeHtmlAttr(__('Reduce the quantity')); ?>">-</button></span>
                <input type="number"
                       name="qty"
                       id="qty"
                       min="0"
                       value="<?= $block->getProductDefaultQty() * 1 ?>"
                       title="<?= $block->escapeHtmlAttr(__('Qty')) ?>"
                       class="input-text qty"
                       data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
                       />
                       <span class="plus"><button type="button" title="<?= $block->escapeHtmlAttr(__('Increase the quantity')); ?>"/>+</button></span>
                       <script type="text/javascript">
                        // This is the javascript codes help us to increase and decrease qty
                        require(['jquery'], function ($) {
                            $('.box-tocart .minus').on('click', function () {
                                var qty = parseInt($('#qty').val());
                                qty = qty - 1;
                                $('#qty').val(qty).trigger('change');
                            });
                            $('.box-tocart .plus').on('click', function () {
                                var qty = parseInt($('#qty').val());
                                qty = qty + 1;
                                $('#qty').val(qty).trigger('change');
                            });
                            $('#qty').on('change', function () {
                                var qty = parseInt($(this).val());
                                if (qty > 100) {
                                    $(this).val('100');
                                } else if (qty < 1) {
                                    $(this).val('1');
                                }
                            });
                        });
                    </script>
            </div>
        </div>
        <?php endif; ?>
        <div class="actions">
            <button type="submit"
                    title="<?= $block->escapeHtmlAttr($buttonTitle) ?>"
                    class="action primary tocart"
                    id="product-addtocart-button" disabled>
                <span><?= $block->escapeHtml($buttonTitle) ?></span>
            </button>
            <?= $block->getChildHtml('', true) ?>
        </div>
    </div>
</div>
<?php endif; ?>
<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "Magento_Catalog/js/validate-product": {}
        }
    }
</script>
Now we will check how to auto open minicart after products added into the cart.
For that we will modify in app/code/Magelearn/AdvanceCart/view/frontend/layout/default.xml file.
you can check the vendor/magento/module-checkout/view/frontend/layout/default.xml for referance.

<?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">
    <head>
		<css src="Magelearn_AdvanceCart::css/minicart.css" order="100"/>
		<link src="Magelearn_AdvanceCart::js/minicart-data.js"/>
	</head>
    <body>
    	<referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="minicart.autoopen" template="Magelearn_AdvanceCart::minicart_open.phtml"/>
        </referenceContainer>
        <referenceBlock name="minicart">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="types" xsi:type="array"/>
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                        	<item name="component" xsi:type="string">Magento_Checkout/js/view/minicart</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magelearn_AdvanceCart/minicart/content</item>
                            </item>
                            <item name="children" xsi:type="array">
                                <item name="promotion" xsi:type="array">
                                    <item name="component" xsi:type="string">Magento_Checkout/js/view/checkout/minicart/subtotal/totals</item>
                                    <item name="config" xsi:type="array">
                                        <item name="template" xsi:type="string">Magelearn_AdvanceCart/minicart/total/grand-total</item>
                                    </item>
                                </item>
                            </item>                      
                        </item>
                        <item name="coupon" xsi:type="array">
                            <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/coupon</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magelearn_AdvanceCart/coupon</item>
                            </item>
                        </item>
                        <item name="totals" xsi:type="array">
                            <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/totals</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magelearn_AdvanceCart/totals</item>
                            </item>
                        </item>
                        <item name="sliding-cart" xsi:type="array">
                            <item name="component" xsi:type="string">Magelearn_AdvanceCart/js/sliding-cart</item>
                        </item>
                    </item>
                </argument>
            </arguments>
     </referenceBlock>
    </body>
</page>

As per highlighted in above xml file, we will add our template file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart_open.phtml

<script type="text/x-magento-init">
{
     "[data-block='minicart']" : {
        "Magelearn_MinicartOpen/js/view/minicartopen" : {}
     }
}
Now will call our JS component file.
Create app/code/Magelearn/AdvanceCart/view/frontend/web/js/view/minicartopen.js file.

define(["jquery/ui", "jquery"], function(Component, $) {
	return function(config, element) {
		var minicart = $(element);
		minicart.on('contentLoading', function() {
			minicart.on('contentUpdated', function() {
				minicart.find('[data-role="dropdownDialog"]').dropdownDialog("open");
			});
		});
	}
});

Now we will check how to add product Short description in minicart items.

For that first we will add app/code/Magelearn/AdvanceCart/etc/catalog_attributes.xml file.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd">
    <group name="quote_item">
		<attribute name="short_description"/>
	 </group>
</config>

Now we will add our plugin file at app/code/Magelearn/AdvanceCart/etc/frontend/di.xml to add additional data into the cart items. 

We can also do the same thing with preference but plugin is more preferable over preference.

<?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\CustomerData\Cart">
        <plugin name="magelearn_advancecart_grand_total" type="Magelearn\AdvanceCart\Plugin\Checkout\CustomerData\Cart"/>
 	</type>
    <type name="Magento\Checkout\CustomerData\DefaultItem">
        <plugin name="AddDesPlug" type="Magelearn\AdvanceCart\Plugin\DefaultItem" disabled="false" sortOrder="10"/>
    </type>
</config>

Now create a plugin file at app/code/Magelearn/AdvanceCart/Plugin/DefaultItem.php and add additional data for product's items.

<?php
namespace Magelearn\AdvanceCart\Plugin;
use Magento\Quote\Model\Quote\Item;

class DefaultItem
{
    public function aroundGetItemData($subject, \Closure $proceed, Item $item)
    {
        $data = $proceed($item);
        $atts = [];
        
        $product_desc = $item->getProduct()->getShortDescription(); 
 
        $atts = [
            "short_description" => $product_desc
        ];
 
        return array_merge($data, $atts);
    }
}

Now we will override vendor/magento/module-checkout/view/frontend/web/template/minicart/item/default.html file with our custom module file.

For that we need to add entry in requirejs-config.js file.
Add app/code/Magelearn/AdvanceCart/view/frontend/requirejs-config.js file.

var config = {
	paths: {            
    	'slick': "Magelearn_AdvanceCart/js/slick"
    },   
    map: {
        '*': {
        	'Magento_Checkout/template/minicart/item/default.html': 'Magelearn_AdvanceCart/template/minicart/item/default.html',
            'sidebar': 'Magelearn_AdvanceCart/js/sidebar',
            'Magento_Checkout/js/view/minicart': 'Magelearn_AdvanceCart/js/view/minicart'
        }
    },
    shim: {
		'slick': {
	    	deps: ['jquery']
	    }
	}
};

Now add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/item/default.html file.

<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<li class="item product product-item" data-role="product-item">
    <div class="product">
        <!-- ko if: product_has_url -->
        <a data-bind="attr: {href: product_url, title: product_name}" tabindex="-1" class="product-item-photo">
            <!-- ko foreach: $parent.getRegion('itemImage') -->
                <!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko -->
            <!-- /ko -->
        </a>
        <!-- /ko -->
        <!-- ko ifnot: product_has_url -->
        <span class="product-item-photo">
            <!-- ko foreach: $parent.getRegion('itemImage') -->
                <!-- ko template: {name: getTemplate(), data: item.product_image} --><!-- /ko -->
            <!-- /ko -->
        </span>
        <!-- /ko -->

        <div class="product-item-details">
            <strong class="product-item-name">
                <!-- ko if: product_has_url -->
                <a data-bind="attr: {href: product_url}, html: $parent.getProductNameUnsanitizedHtml(product_name)"></a>
                <!-- /ko -->
                <!-- ko ifnot: product_has_url -->
                    <span data-bind="html: $parent.getProductNameUnsanitizedHtml(product_name)"></span>
                <!-- /ko -->
                <p class="productDesc">
				 <!-- ko if: short_description -->
                    <span data-bind="html: $parent.getProductNameUnsanitizedHtml(short_description)"></span>
                <!-- /ko -->
				</p>
            </strong>

            <!-- ko if: options.length -->
            <div class="product options" data-mage-init='{"collapsible":{"openedState": "active", "saveState": false}}'>
                <span data-role="title" class="toggle"><!-- ko i18n: 'See Details' --><!-- /ko --></span>

                <div data-role="content" class="content">
                    <strong class="subtitle"><!-- ko i18n: 'Options Details' --><!-- /ko --></strong>
                    <dl class="product options list">
                        <!-- ko foreach: { data: options, as: 'option' } -->
                        <dt class="label"><!-- ko text: option.label --><!-- /ko --></dt>
                        <dd class="values">
                            <!-- ko if: Array.isArray(option.value) -->
                                <span data-bind="html: $parents[1].getOptionValueUnsanitizedHtml(option.value.join('<br/>'))"></span>
                            <!-- /ko -->
                            <!-- ko if: (!Array.isArray(option.value) && ['file', 'html'].includes(option.option_type)) -->
                                <span data-bind="html: $parents[1].getOptionValueUnsanitizedHtml(option.value)"></span>
                            <!-- /ko -->
                            <!-- ko if: (!Array.isArray(option.value) && !['file', 'html'].includes(option.option_type)) -->
                            <span data-bind="text: option.value"></span>
                            <!-- /ko -->
                        </dd>
                        <!-- /ko -->
                    </dl>
                </div>
            </div>
            <!-- /ko -->

            <div class="product-item-pricing">
                <!-- ko if: canApplyMsrp -->

                <div class="details-map">
                    <span class="label" data-bind="i18n: 'Price'"></span>
                    <span class="value" data-bind="i18n: 'See price before order confirmation.'"></span>
                </div>
                <!-- /ko -->
                <!-- ko ifnot: canApplyMsrp -->
                <!-- ko foreach: $parent.getRegion('priceSidebar') -->
                    <!-- ko template: {name: getTemplate(), data: item.product_price, as: 'price'} --><!-- /ko -->
                <!-- /ko -->
                <!-- /ko -->

                <div class="details-qty qty">
                    <label class="label" data-bind="i18n: 'Qty', attr: {
                           for: 'cart-item-'+item_id+'-qty'}"></label>
                    <button type="button" data-bind="attr: {title: $t('Decrease the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" class="decreasing-qty">-</button>
                    <input data-bind="attr: { id: 'cart-item-'+item_id+'-qty', 'data-cart-item': item_id,  'data-item-qty': qty, 'data-cart-item-id': product_sku}, value: qty"
                            type="number"
                            size="4"
                            class="item-qty cart-item-qty"
                            maxlength="12"/>
                    <button type="button" data-bind="attr: {title: $t('Increase the quantity'), 'data-cart-item': item_id, 'data-item-qty': qty}" class="increasing-qty">+</button>
                    <button data-bind="attr: { id: 'update-cart-item-'+item_id, 'data-cart-item': item_id, title: $t('Update') }"
                            class="update-cart-item"
                            style="display: none">
                        <span data-bind="i18n: 'Update'"></span>
                    </button>
                </div>
            </div>

            <div class="product actions">
                <!-- ko if: is_visible_in_site_visibility -->
                <div class="primary">
                    <a data-bind="attr: {href: configure_url, title: $t('Edit item')}" class="action edit">
                        <span data-bind="i18n: 'Edit'"></span>
                    </a>
                </div>
                <!-- /ko -->
                <div class="secondary">
                    <a href="#" data-bind="attr: {'data-cart-item': item_id, title: $t('Remove item')}"
                       class="action delete">
                        <span data-bind="i18n: 'Remove'"></span>
                    </a>
                </div>
            </div>
        </div>
    </div>
    <div class="message notice" if="$data.message">
        <div data-bind="text: $data.message"></div>
    </div>
</li>

Now as per defined in requirejs-config.js file we will add app/code/Magelearn/AdvanceCart/view/frontend/web/js/view/minicart.js file.

define([
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'jquery',
    'ko',
    'underscore',
    'sidebar',
    'mage/translate',
    'mage/dropdown'
], function (Component, customerData, $, ko, _) {
    'use strict';

    var sidebarInitialized = false,
        addToCartCalls = 0,
        miniCart;

    miniCart = $('[data-block=\'minicart\']');

    /**
     * @return {Boolean}
     */
    function initSidebar() {
        if (miniCart.data('mageSidebar')) {
            miniCart.sidebar('update');
        }

        if (!$('[data-role=product-item]').length) {
            return false;
        }
        miniCart.trigger('contentUpdated');

        if (sidebarInitialized) {
            return false;
        }
        sidebarInitialized = true;
        miniCart.sidebar({
            'targetElement': 'div.block.block-minicart',
            'url': {
                'checkout': window.checkout.checkoutUrl,
                'update': window.checkout.updateItemQtyUrl,
                'remove': window.checkout.removeItemUrl,
                'loginUrl': window.checkout.customerLoginUrl,
                'isRedirectRequired': window.checkout.isRedirectRequired
            },
            'button': {
                'checkout': '#top-cart-btn-checkout',
                'remove': '#mini-cart a.action.delete',
                'close': '#btn-minicart-close'
            },
            'showcart': {
                'parent': 'span.counter',
                'qty': 'span.counter-number',
                'label': 'span.counter-label'
            },
            'minicart': {
                'list': '#mini-cart',
                'content': '#minicart-content-wrapper',
                'qty': 'div.items-total',
                'subtotal': 'div.subtotal span.price',
                'maxItemsVisible': window.checkout.minicartMaxItemsVisible
            },
            'item': {
                'qty': ':input.cart-item-qty',
                'button': ':button.update-cart-item',
                'qtyDecreasing': '.decreasing-qty',
                'qtyIncreasing': '.increasing-qty'
            },
            'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart?')
        });
    }
	miniCart.on('dropdowndialogopen', function () {
        initSidebar();
    });
    return Component.extend({
        shoppingCartUrl: window.checkout.shoppingCartUrl,
        maxItemsToDisplay: window.checkout.maxItemsToDisplay,
        cart: {},

        /**
         * @override
         */
        initialize: function () {
            var self = this,
                cartData = customerData.get('cart');

            this.update(cartData());
            cartData.subscribe(function (updatedCart) {
                addToCartCalls--;
                this.isLoading(addToCartCalls > 0);
                sidebarInitialized = false;
                this.update(updatedCart);
                initSidebar();
            }, this);
            $('[data-block="minicart"]').on('contentLoading', function (event) {
                addToCartCalls++;
                self.isLoading(true);
            });
            if (cartData().website_id !== window.checkout.websiteId) {
                customerData.reload(['cart'], false);
            }

            return this._super();
        },
        isLoading: ko.observable(false),
        initSidebar: initSidebar,

        /**
         * Close mini shopping cart.
         */
        closeMinicart: function () {
            $('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog('close');
        },

        /**
         * @param {String} productType
         * @return {*|String}
         */
        getItemRenderer: function (productType) {
            return this.itemRenderer[productType] || 'defaultRenderer';
        },

        /**
         * Update mini shopping cart content.
         *
         * @param {Object} updatedCart
         * @returns void
         */
        update: function (updatedCart) {
            _.each(updatedCart, function (value, key) {
                if (!this.cart.hasOwnProperty(key)) {
                    this.cart[key] = ko.observable();
                }
                this.cart[key](value);
            }, this);
        },
		/**
         * Get cart param by name.
         *
         * @param {String} name
         * @returns {*}
         */
        getCartParamUnsanitizedHtml: function (name) {
            if (!_.isUndefined(name)) {
                if (!this.cart.hasOwnProperty(name)) {
                    this.cart[name] = ko.observable();
                }
            }

            return this.cart[name]();
        },
        /**
         * Get cart param by name.
         *
         * @param {String} name
         * @returns {*}
         */
        getCartParam: function (name) {
            return this.getCartParamUnsanitizedHtml(name);
        },

        /**
         * Returns array of cart items, limited by 'maxItemsToDisplay' setting.
         *
         * @returns []
         */
        getCartItems: function () {
            var items = this.getCartParam('items') || [];
            items = items.slice(parseInt(-this.maxItemsToDisplay, 10));

            return items;
        },

        /**
         * Returns count of cart line items.
         *
         * @returns {Number}
         */
        getCartLineItemsCount: function () {
            var items = this.getCartParam('items') || [];

            return parseInt(items.length, 10);
        }
    });
});

Now as per defined in requirejs-config.js file we will add our JS component file at
app/code/Magelearn/AdvanceCart/view/frontend/web/js/sidebar.js to add +/- button functionality in minicart items.

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define([
    'jquery',
    'Magento_Customer/js/model/authentication-popup',
    'Magento_Customer/js/customer-data',
    'Magento_Ui/js/modal/alert',
    'Magento_Ui/js/modal/confirm',
    'underscore',
    'jquery-ui-modules/widget',
    'mage/decorate',
    'mage/collapsible',
    'mage/cookies',
    'jquery-ui-modules/effect-fade'
], function ($, authenticationPopup, customerData, alert, confirm, _) {
    'use strict';

    $.widget('mage.sidebar', {
        options: {
            isRecursive: true,
            minicart: {
                maxItemsVisible: 3
            }
        },
        scrollHeight: 0,
        shoppingCartUrl: window.checkout.shoppingCartUrl,

        /**
         * Create sidebar.
         * @private
         */
        _create: function () {
            this._initContent();
        },

        /**
         * Update sidebar block.
         */
        update: function () {
            $(this.options.targetElement).trigger('contentUpdated');
            this._calcHeight();
            this._isOverflowed();
        },

        /**
         * @private
         */
        _initContent: function () {
            var self = this,
                events = {};

            this.element.decorate('list', this.options.isRecursive);

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.button.close] = function (event) {
                event.stopPropagation();
                $(self.options.targetElement).dropdownDialog('close');
            };
            events['click ' + this.options.button.checkout] = $.proxy(function () {
                var cart = customerData.get('cart'),
                    customer = customerData.get('customer'),
                    element = $(this.options.button.checkout);

                if (!customer().firstname && cart().isGuestCheckoutAllowed === false) {
                    // set URL for redirect on successful login/registration. It's postprocessed on backend.
                    $.cookie('login_redirect', this.options.url.checkout);

                    if (this.options.url.isRedirectRequired) {
                        element.prop('disabled', true);
                        location.href = this.options.url.loginUrl;
                    } else {
                        authenticationPopup.showModal();
                    }

                    return false;
                }
                element.prop('disabled', true);
                location.href = this.options.url.checkout;
            }, this);

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.button.remove] =  function (event) {
                event.stopPropagation();
                confirm({
                    content: self.options.confirmMessage,
                    actions: {
                        /** @inheritdoc */
                        confirm: function () {
                            self._removeItem($(event.currentTarget));
                        },

                        /** @inheritdoc */
                        always: function (e) {
                            e.stopImmediatePropagation();
                        }
                    }
                });
            };

            /**
             * @param {jQuery.Event} event
             */
            events['keyup ' + this.options.item.qty] = function (event) {
                self._showItemButton($(event.target));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['change ' + this.options.item.qty] = function (event) {
                self._showItemButton($(event.target));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['click ' + this.options.item.button] = function (event) {
                event.stopPropagation();
                self._updateItemQty($(event.currentTarget));
            };

            /**
             * @param {jQuery.Event} event
             */
            events['focusout ' + this.options.item.qty] = function (event) {
                self._validateQty($(event.currentTarget));
            };
			
			// The bellow codes will execute when you click on the decrease button
            events['click ' + this.options.item.qtyDecreasing] = function (event) {
                event.stopPropagation();
                var itemId = $(event.currentTarget).data('cart-item');
                var qtyElement = $('#cart-item-' + itemId + '-qty');
                var qtyValue = parseInt(qtyElement.val());
                qtyValue = qtyValue - 1;
                if (qtyValue <= 0) {
                    qtyValue = 1;
                }
                qtyElement.val(qtyValue).trigger('keyup');
            };

            // The bellow codes will execute when you click on the increase button
            events['click ' + this.options.item.qtyIncreasing] = function (event) {
                event.stopPropagation();
                var itemId = $(event.currentTarget).data('cart-item');
                var qtyElement = $('#cart-item-' + itemId + '-qty');
                var qtyValue = parseInt(qtyElement.val());
                qtyValue = qtyValue + 1;
                if (qtyValue > 100) {
                    qtyValue = 100;
                }
                qtyElement.val(qtyValue).trigger('keyup');
            };
            
            this._on(this.element, events);
            this._calcHeight();
            this._isOverflowed();
        },
		/**
         * Add 'overflowed' class to minicart items wrapper element
         *
         * @private
         */
        _isOverflowed: function () {
            var list = $(this.options.minicart.list),
                cssOverflowClass = 'overflowed';

            if (this.scrollHeight > list.innerHeight()) {
                list.parent().addClass(cssOverflowClass);
            } else {
                list.parent().removeClass(cssOverflowClass);
            }
        },
        
        /**
         * @param {HTMLElement} elem
         * @private
         */
        _showItemButton: function (elem) {
            var itemId = elem.data('cart-item'),
                itemQty = elem.data('item-qty');

            if (this._isValidQty(itemQty, elem.val())) {
                $('#update-cart-item-' + itemId).show('fade', 300);
            } else if (elem.val() == 0) { //eslint-disable-line eqeqeq
                this._hideItemButton(elem);
            } else {
                this._hideItemButton(elem);
            }
        },

        /**
         * @param {*} origin - origin qty. 'data-item-qty' attribute.
         * @param {*} changed - new qty.
         * @returns {Boolean}
         * @private
         */
        _isValidQty: function (origin, changed) {
            return origin != changed && //eslint-disable-line eqeqeq
                changed.length > 0 &&
                changed - 0 == changed && //eslint-disable-line eqeqeq
                changed - 0 > 0;
        },

        /**
         * @param {Object} elem
         * @private
         */
        _validateQty: function (elem) {
            var itemQty = elem.data('item-qty');

            if (!this._isValidQty(itemQty, elem.val())) {
                elem.val(itemQty);
            }
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _hideItemButton: function (elem) {
            var itemId = elem.data('cart-item');

            $('#update-cart-item-' + itemId).hide('fade', 300);
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _updateItemQty: function (elem) {
            var itemId = elem.data('cart-item');

            this._ajax(this.options.url.update, {
                'item_id': itemId,
                'item_qty': $('#cart-item-' + itemId + '-qty').val()
            }, elem, this._updateItemQtyAfter);
        },

        /**
         * Update content after update qty
         *
         * @param {HTMLElement} elem
         */
        _updateItemQtyAfter: function (elem) {
            var productData = this._getProductById(Number(elem.data('cart-item')));

            if (!_.isUndefined(productData)) {
                $(document).trigger('ajax:updateCartItemQty');

                if (window.location.href === this.shoppingCartUrl) {
                    window.location.reload(false);
                }
            }
            this._hideItemButton(elem);
        },

        /**
         * @param {HTMLElement} elem
         * @private
         */
        _removeItem: function (elem) {
            var itemId = elem.data('cart-item');

            this._ajax(this.options.url.remove, {
                'item_id': itemId
            }, elem, this._removeItemAfter);
        },

        /**
         * Update content after item remove
         *
         * @param {Object} elem
         * @private
         */
        _removeItemAfter: function (elem) {
            var productData = this._getProductById(Number(elem.data('cart-item')));

            if (!_.isUndefined(productData)) {
                $(document).trigger('ajax:removeFromCart', {
                    productIds: [productData['product_id']],
                    productInfo: [
                        {
                            'id': productData['product_id']
                        }
                    ]
                });

                if (window.location.href.indexOf(this.shoppingCartUrl) === 0) {
                    window.location.reload();
                }
            }
        },

        /**
         * Retrieves product data by Id.
         *
         * @param {Number} productId - product Id
         * @returns {Object|undefined}
         * @private
         */
        _getProductById: function (productId) {
            return _.find(customerData.get('cart')().items, function (item) {
                return productId === Number(item['item_id']);
            });
        },

        /**
         * @param {String} url - ajax url
         * @param {Object} data - post data for ajax call
         * @param {Object} elem - element that initiated the event
         * @param {Function} callback - callback method to execute after AJAX success
         */
        _ajax: function (url, data, elem, callback) {
            $.extend(data, {
                'form_key': $.mage.cookies.get('form_key')
            });

            $.ajax({
                url: url,
                data: data,
                type: 'post',
                dataType: 'json',
                context: this,

                /** @inheritdoc */
                beforeSend: function () {
                    elem.attr('disabled', 'disabled');
                },

                /** @inheritdoc */
                complete: function () {
                    elem.attr('disabled', null);
                }
            })
                .done(function (response) {
                    var msg;

                    if (response.success) {
                        callback.call(this, elem, response);
                    } else {
                        msg = response['error_message'];

                        if (msg) {
                            alert({
                                content: msg
                            });
                        }
                    }
                })
                .fail(function (error) {
                    console.log(JSON.stringify(error));
                });
        },

        /**
         * Calculate height of minicart list
         *
         * @private
         */
        _calcHeight: function () {
            var self = this,
                height = 0,
                counter = this.options.minicart.maxItemsVisible,
                target = $(this.options.minicart.list),
                outerHeight;

            self.scrollHeight = 0;
            target.children().each(function () {

                if ($(this).find('.options').length > 0) {
                    $(this).collapsible();
                }
                outerHeight = $(this).outerHeight(true);

                if (counter-- > 0) {
                    height += outerHeight;
                }
                self.scrollHeight += outerHeight;
            });

            target.parent().height(height);
        }
    });

    return $.mage.sidebar;
});

Now as per defined in etc/frontend/di.xml file we will add our plugin file to add additional data into the cart object.

Create file app/code/Magelearn/AdvanceCart/Plugin/Checkout/CustomerData/Cart.php file.

<?php
namespace Magelearn\AdvanceCart\Plugin\Checkout\CustomerData;

use Magelearn\AdvanceCart\Helper\Data as helperData;

class Cart
{
	/**
     * @var Magelearn\AdvanceCart\Helper\Data
     */
    protected $helperData;
	
    protected $layoutFactory;
	
	protected $checkoutSession;
    protected $checkoutHelper;
    protected $quote;
	/**
     * @var \Magento\Quote\Api\CartTotalRepositoryInterface
     */
    protected $cartTotalRepository;
	
	/**
     * @var \Magento\Framework\Pricing\Helper\Data
     */
    protected $priceHelper;
	
	/**
     * @var \Magento\Quote\Model\QuoteIdMaskFactory
     */
    protected $quoteIdMaskFactory;
	
	/**
     * @var \Magento\Customer\Model\Session
     */
    protected $customerSession;
	/**
     * @var \Magento\Framework\UrlInterface
     */
    protected $urlInterface;
	protected $_logger;
	
    public function __construct(
        \Magento\Framework\View\LayoutFactory $layoutFactory,
        helperData $helperData,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Magento\Checkout\Helper\Data $checkoutHelper,
        \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalRepository,
        \Magento\Framework\Pricing\Helper\Data $priceHelper,
        \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Framework\UrlInterface $urlInterface,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->layoutFactory = $layoutFactory;
		$this->helperData = $helperData;
		$this->checkoutSession = $checkoutSession;
        $this->checkoutHelper = $checkoutHelper;
		$this->cartTotalRepository = $cartTotalRepository;
		$this->priceHelper = $priceHelper;
		$this->quoteIdMaskFactory = $quoteIdMaskFactory;
		$this->customerSession = $customerSession;
		$this->urlInterface = $urlInterface;
		$this->_logger = $logger;
    }

    /**
     * Add grand total to result
     *
     * @param \Magento\Checkout\CustomerData\Cart $subject
     * @param array $result
     * @return array
     */
    public function afterGetSectionData(
        \Magento\Checkout\CustomerData\Cart $subject,
        $result
    ) {
    	$upsell_view = $this->helperData->getGeneralConfig('upsell_view');
		if($upsell_view == 1) {
			$upselldata = $this->layoutFactory->create()
	          ->createBlock(\Magelearn\AdvanceCart\Block\Cart\Upsell::class)
	          ->setTemplate('Magelearn_AdvanceCart::minicart/slider.phtml')
	          ->toHtml();
	        $result['advancecart']['upsell_data'] = $upselldata;
		} else {
	        $upselldata = $this->layoutFactory->create()
	          ->createBlock(\Magelearn\AdvanceCart\Block\Cart\Upsell::class)
	          ->setTemplate('Magelearn_AdvanceCart::minicart/grid.phtml')
	          ->toHtml();
	        $result['advancecart']['upsell_data'] = $upselldata;
		}
		
		$result['advancecart']['coupon_enable'] = boolval($this->helperData->getGeneralConfig('coupon'));
        $result['advancecart']['totals_enable'] = boolval($this->helperData->getGeneralConfig('totals'));
        $result['advancecart']['totals_title'] = $this->helperData->getGeneralConfig('totals_title');
		
		$coupon_code = $this->getQuote()->getCouponCode();
		$result['advancecart']['coupon_code'] = ($coupon_code != '' ? $coupon_code : null);
		
		$quoteId = $this->getQuote()->getId();
		$quoteIdMask = $this->quoteIdMaskFactory->create()->load($quoteId, 'quote_id');
		
		if(isset($quoteId) && $quoteId != null) {
			$totals = $this->cartTotalRepository->get($quoteId)->getTotalSegments();
		} else {
			$totals = '';
		}
		
		$_totals = [];
		
		if(isset($totals) && $totals != '') {
	        foreach ($totals as $key => $value) {
	            $_totals[] = [
	                'title' => $value->getTitle(),
	                'value' => $this->priceHelper->currency($value->getValue(), true, false),
	            ];
	        }
        }
		
		$result['advancecart']['totals'] = $_totals;
		$result['advancecart']['grand_total'] = isset($totals['grand_total'])
            ? $this->priceHelper->currency($totals['grand_total']->getValue(), true, false)
            : 0;
			
		$isLoggedIn = $this->customerSession->isLoggedIn();
		$result['advancecart']['isLoggedIn'] = ($isLoggedIn != '' ? true : false);
		$result['advancecart']['apiUrl'] = $this->urlInterface->getBaseUrl().'rest/default/V1/';
		$result['advancecart']['quoteId'] = $quoteIdMask->getMaskedId();
        
        return $result;
    }
	/**
     * Get active quote.
     *
     * @return Quote
     */
    protected function getQuote()
    {
        if ($this->quote === null) {
            $this->quote = $this->checkoutSession->getQuote();
        }

        return $this->quote;
    }
}

Now add app/code/Magelearn/AdvanceCart/Helper/Data.php file.

<?php

namespace Magelearn\AdvanceCart\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;

class Data extends AbstractHelper
{
    const XML_PATH_ADVANCECART = 'advancecart_setting/';

    /**
     * Get ConfigValue.
     *
     * @return {Boolean}
     */
    public function getConfigValue($field, $storeId = null)
    {
        return $this->scopeConfig->getValue(
            $field,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    /**
     * Get GeneralConfig.
     *
     * @return {String}
     */
    public function getGeneralConfig($code, $storeId = null)
    {
        return $this->getConfigValue(self::XML_PATH_ADVANCECART.'minicart_settings/'.$code, $storeId);
    }
}

Now we will create our block and template files to get Up-sell items data and display it.

Create app/code/Magelearn/AdvanceCart/Block/Cart/Upsell.php file.

<?php
namespace Magelearn\AdvanceCart\Block\Cart;

/**
 * Upsell content block
 */
class Upsell extends \Magento\Framework\View\Element\Template
{
    protected $_productCollectionFactory;
    protected $_imageHelper;
    protected $_cartHelper;
    protected $_imageBuilder;
    protected $_scopeConfig;
    protected $_productRepository;
    protected $_checkoutSession;
    protected $_cart;
    protected $_storeScope;
    protected $_priceHelper;
    protected $_formKey;
	protected $_logger;
    
    const XML_PATH_KEY = 'advancecart_setting/minicart_settings/';

    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Catalog\Block\Product\Context $productcontext,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Checkout\Model\Cart $cart,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
        \Magento\Framework\Pricing\Helper\Data $priceHelper,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        \Magento\Framework\Data\Form\FormKey $formKey,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->_productCollectionFactory = $productCollectionFactory;
        $this->_imageHelper = $productcontext->getImageHelper();
        $this->_imageBuilder = $productcontext->getImageBuilder();
        $this->_cartHelper = $productcontext->getCartHelper();
        $this->_scopeConfig = $scopeConfig;
        $this->_cart = $cart;
        $this->_checkoutSession = $checkoutSession;
        $this->_productRepository = $productRepository;
        $this->_storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
        $this->_priceHelper = $priceHelper;
        $this->_formKey = $formKey;
		$this->_logger = $logger;

        parent::__construct($context);
    }
    
    public function getTitle()
    {
        $upsell_title = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_title', $this->_storeScope);
        return $upsell_title;
    }
    public function getProductCollection()
    {
        $upsell_limit = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_limit', $this->_storeScope);
        $upsell_sku = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_sku', $this->_storeScope);
        $upsell_type = $this->_scopeConfig->getValue(self::XML_PATH_KEY.'upsell_type', $this->_storeScope);
        
        $ids = [];
        $productInfo = $this->_cart->getQuote()->getItemsCollection();
		foreach ($productInfo as $item){
       		$ids[] = $item['product_id'];
    	}
		
        $sku = [];
		
        if ($upsell_sku != '') {
            if (strpos($upsell_sku, ',') !== false) {
                $sku1 = explode(',', $upsell_sku);
                foreach ($sku1 as $skuchild) {
                    if (trim($skuchild) != '') {
                        $sku[] = trim($skuchild);
                    }
                }
            } else {
                $sku = [trim($upsell_sku)];
            }
        }
        $collection = $this->_productCollectionFactory->create();
        $collection->addAttributeToSelect('*');
        $collection->addFilter('type_id', 'simple');
        try {
            if ($upsell_type == '1') {
                $ids = $this->getUpsellProductID();
                if (count($ids) > 0) {
                    $collection->addFieldToFilter('entity_id', ['in' => $ids]);
                }
            } else {
                if (count($sku) > 0) {
                    $collection->addFieldToFilter('sku', ['in' => $sku]);
                }
            }

        } catch (\Exception $e) {
            return false;
        }

        $collection->setPageSize($upsell_limit); // fetching only limited products
        return $collection;
    }
    
    public function getUpsellProductID()
    {
        $upsellProduct = [];
        try {
            $productInfo = $this->_cart->getQuote()->getItemsCollection();
            if (count($productInfo) == 0) {
                return $upsellProduct;
            }
            foreach ($productInfo as $item) {
                $product = $this->_productRepository->getById($item->getProductId());
                $upsell = $product->getUpSellProducts();
                if (count($upsell)) {
                    foreach ($upsell as $item) {
                        $upsellProduct[$item->getProductId()][] = $item->getId();
                    }
                }
            }
            if (count($upsellProduct) == 0) {
                return $upsellProduct;
            }
            $finalarray = [];
            $cnt = 0;
            for ($i=0; $i<1000; $i++) {
                foreach ($upsellProduct as $upsellProduct1) {
                    foreach ($upsellProduct1 as $upsellProduct2) {
                        if (!in_array($upsellProduct2, $finalarray)) {
                            $finalarray[]= $array2;
                            break;
                        }
                    }
                }
                $i++;
                if (count($finalarray) >= $cnt) {
                    break;
                }
            }
            return $finalarray;
        } catch (\Exception $e) {
            return $upsellProduct;
        }
        return $upsellProduct;
    }
    public function hasProductUrl($product)
    {
		if(strtolower($product->getAttributeText('visibility')) == 'not visible individually') {
			return false;
		} else {
			return true;
		}
		
        return false;
    }
    public function getProductUrl($product, $additional = [])
    {
        if ($this->hasProductUrl($product)) {
            if (!isset($additional['_escape'])) {
                $additional['_escape'] = true;
            }
            return $product->getUrlModel()->getUrl($product, $additional);
        }

        return '#';
    }

    public function getImage($product, $imageId, $attributes = [])
    {
        return $this->_imageBuilder->create($product, $imageId, $attributes);
    }
    public function getAddToCartUrl($product, $additional = [])
    {
        if (!$product->getTypeInstance()->isPossibleBuyFromList($product)) {
            if (!isset($additional['_escape'])) {
                $additional['_escape'] = true;
            }
            if (!isset($additional['_query'])) {
                $additional['_query'] = [];
            }
            $additional['_query']['options'] = 'cart';

            return $this->getProductUrl($product, $additional);
        }
        return $this->_cartHelper->getAddUrl($product, $additional);
    }
    public function getFormattedCurrency($price)
    {
        return $this->_priceHelper->currency($price, true, false);
    }
    public function getFormKey()
    {
        return $this->_formKey->getFormKey();
    }
}

We can display this upsell products data either in scroll view or with Slider (slick slider) view.
To display this data in simple scroll view create file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart/grid.phtml

<?php
    $image = 'new_products_content_widget_grid';
    $collection = $block->getProductCollection();
    $items = $collection->getItems();

    $showWishlist = true;
    $showCompare = true;
    $showCart = true;
    $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::DEFAULT_VIEW;
    $description = false;
?><?php if ($exist = ($collection && $collection->getSize())): ?>
    <div class="dontForgetWrapper">
        <p class="dontForget text-center">
            <?php if ($block->getTitle()): ?>
                <?= $block->escapeHtml(__($block->getTitle())) ?>
            <?php endif ?>
        </p>
        
        <div class="dontForgetCard d-flex">
        <div class="block widget block-products-list">
        <div class="block-content">
            <div class="products-list custom-top-slider">
                <ol class="product-items">
                    <?php $iterator = 1; ?>
                    <?php foreach ($items as $_item): ?>
                        <?= /* @noEscape */
                        ($iterator++ == 1) ? '<li class="product-item">' : '</li><li class="product-item">' ?>
                        <div class="product-item-info">
                            <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" 
                            class="product-item-photo dontForgetImg">
                                <?= $block->getImage($_item, $image)->toHtml() ?>
                            </a>
                            <div class="product-item-details">
                                 <div class="dontForgetInfoWrapper">
                                    <a title="<?= $block->escapeHtml($_item->getName()) ?>"
                                       href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"
                                       class="product-item-link">
                                    <?php
                                       $product_title = $_item->getName();
                                       $product_type = $_item->getTypeId();
                                       $product_description = $_item->getShortDescription();
                                    if ($product_title) { ?>
                                        <h3 class="productName"><?= /* @noEscape */ $product_title; ?></h3>
                                        <?php } if ($product_type) { ?>
                                        <h6 class="productCategory"><?= /* @noEscape */ $product_type; ?></h6>
                                        <?php }if ($product_description) { ?>
                                        <p class="productMisc"><?= /* @noEscape */ $product_description; ?></p>
                                        <?php  } ?>
                                    </a>
                                </div>
                                <?php
                               
                                if ($showWishlist || $showCompare || $showCart): ?>
                                    <div class="product-item-actions productAddWrapper">
                                        <?php if ($showCart): ?>
                                            <div class="actions-primary addProduct d-flex">
                                                <?php if ($_item->isSaleable()): ?>
<form data-role="minicart-form" class="minicartForm" 
action="<?= /* @noEscape */ $block->getAddToCartUrl($_item);?>" method="post">
<input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" />
<p class="addProduct d-flex">ADD 
<button class="addBtn action tocart primary upsellbutton"  type="submit">
<span><?= $block->escapeHtml(__('+')) ?></span>
</button>
</p>
</form>

                                                <?php endif; ?>
                                            </div>
                                            
                                        <?php endif;
                                            $formattedPrice = $block->getFormattedCurrency($_item->getPrice()); ?>
                                        <div class="upsell_price"><?= /* @noEscape */ $formattedPrice; ?></div>
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>
                        <?= ($iterator == count($items) + 1) ? '</li>' : '' ?>
                    <?php endforeach ?>
                </ol>
            </div>
        </div>
    </div>
</div>
    </diV>
<?php endif;?>

To display data in slider view create file at app/code/Magelearn/AdvanceCart/view/frontend/templates/minicart/slider.phtml

<?php
    $image = 'new_products_content_widget_grid';
    $collection = $block->getProductCollection();
	
    $items = $collection->getItems();
	
    $showWishlist = true;
    $showCompare = true;
    $showCart = true;
    $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::DEFAULT_VIEW;
    $description = false;
?><?php if ($exist = ($collection && $collection->getSize())): ?>
    <div class="dontForgetWrapper">
	        <p class="dontForget text-center">
	            <?php if ($block->getTitle()): ?>
	                <?= $block->escapeHtml(__($block->getTitle())) ?>
	            <?php endif ?>
	        </p>
	        
	        <div class="dontForgetCard d-flex">
	        <div class="block widget block-products-list">
	        <div class="block-content">
	            <div class="products-list custom-top-slider">
	            	<?php foreach ($items as $_item): ?>
	            		<div class="item">
	            			<a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-photo dontForgetImg">
	                            <?= $block->getImage($_item, $image)->toHtml(); ?>
	                        </a>
	                        <div class="item-details">
	                        	<div class="dontForgetInfoWrapper">
	                                <a title="<?= $block->escapeHtml($_item->getName()) ?>"
	                                   href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"
	                                   class="product-item-link">
	                                <?php
	                                   $product_title = $_item->getName();
	                                   $product_type = $_item->getTypeId();
	                                   $product_description = $_item->getShortDescription();
									?>
	                                <?php if ($product_title): ?>
	                                    <p class="productName"><?= /* @noEscape */ $product_title; ?></p>
	                                <?php endif; ?>
	                                </a>
	                                <?php if ($product_type): ?>
	                                    <p class="productCategory"><?= /* @noEscape */ $product_type; ?></p>
	                                <?php endif; ?>
	                                <?php if ($product_description): ?>
	                                    <p class="productMisc"><?= /* @noEscape */ $product_description; ?></p>
	                                <?php endif; ?>
	                            </div>
	                            <?php
	                            if ($showWishlist || $showCompare || $showCart): ?>
	                                <div class="product-item-actions productAddWrapper">
	                                    <?php if ($showCart): ?>
	                                        <div class="actions-primary addProduct d-flex">
	                                            <?php if ($_item->isSaleable()): ?>
	<form data-role="minicart-form" class="minicartForm" 
	action="<?= /* @noEscape */ $block->getAddToCartUrl($_item);?>" method="post">
	<input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" />
	<p class="addProduct d-flex">ADD 
	<button class="addBtn action tocart primary upsellbutton"  type="submit">
	<span><?= $block->escapeHtml(__('+')) ?></span>
	</button>
	</p>
	</form>
	
	                                            <?php endif; ?>
	                                        </div>
	                                        
	                                    <?php endif;
	                                        $formattedPrice = $block->getFormattedCurrency($_item->getPrice()); ?>
	                                    <div class="upsell_price"><?= /* @noEscape */ $formattedPrice; ?></div>
	                                </div>
	                            <?php endif; ?>
	                        </div>
	                    </div>
	            	<?php endforeach ?>
	            </div>
	        </div>
	    </div>
	</div>
</div>
<?php endif;?>
<script>
 require(["jquery","slick"],function($,slick){
     $(document).ready(function(){
     	$('.custom-top-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>

Now to display this upsell products items data into slick slider, we already defined its dependancy in requirejs-config.js file.

As per it create file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/slick.js

Now as per defined in view/frontend/layout/default.xml file, to add this upsell products items into the cart, we will add minicart-data.js file.

Add app/code/Magelearn/AdvanceCart/view/frontend/web/js/minicart-data.js

// Ready function
require(['jquery'], function ($) {
    $(document).ready(function (e) {
		$(document).on('click', '.minicart-wrapper .addProduct:not(.disabled)', function(e) {
			e.preventDefault();
			var formele = $(this).closest("form");
			var keyval = formele.find('input').val();
			if(!keyval){
				return false;
			}
			$(this).addClass('disabled');
			var formData = new FormData();    
			formData.append( 'form_key', formele.find('input').val());
			$.ajax({
                url: formele.attr('action'),
                data: formData,
                type: 'post',
                dataType: 'json',
                cache: false,
                contentType: false,
                processData: false,
                success: function (res) {
					setTimeout(function(){
						if(!$('.minicart-wrapper').hasClass('active')){
							$('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog("open");
						}
					},3000);
					
					$(this).removeClass('disabled');
                },

                /** @inheritdoc */
                error: function (res) {
                    $(this).removeClass('disabled');
                },

                /** @inheritdoc */
                complete: function (res) {
                    $(this).removeClass('disabled');
                }
            });
			return false;
		});
	})
})
Now as per defined in view/frontend/layout/default.xml file, we will add our CSS files to display this data properly.

Now we will start to modify content in Minicart.
For that as per defined in 
view/frontend/layout/default.xml file, we will add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/content.html file.

<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<div class="block-title">
    <strong>
        <span class="text" translate="'My Cart'"></span>
        <span
            class="qty empty"
            text="getCartParam('summary_count')"
            data-bind="css: { empty: !!getCartParam('summary_count') == false },
                       attr: { title: $t('Items in Cart') }">
        </span>
    </strong>
</div>

<div class="block-content">
	<div class="extra">
 		<span class="price-wrapper freeShipping text-center active" data-bind="html: getCartParamUnsanitizedHtml('advancecart.extra_data')"></span>
	</div>
    <button type="button"
            id="btn-minicart-close"
            class="action close"
            data-action="close"
            data-bind="
                attr: {
                    title: $t('Close')
                },
                click: closeMinicart()
            ">
        <span translate="'Close'"></span>
    </button>

    <if args="getCartParam('summary_count')">
        <div class="items-total">
            <span class="count" if="maxItemsToDisplay < getCartLineItemsCount()" text="maxItemsToDisplay"></span>
            <translate args="'of'" if="maxItemsToDisplay < getCartLineItemsCount()"></translate>
            <span class="count" text="getCartParam('summary_count')"></span>
                <!-- ko if: (getCartParam('summary_count') > 1) -->
                    <span translate="'Items in Cart'"></span>
                <!--/ko-->
                <!-- ko if: (getCartParam('summary_count') === 1) -->
                    <span translate="'Item in Cart'"></span>
                <!--/ko-->
        </div>

        <each args="getRegion('subtotalContainer')" render=""></each>
        <each args="getRegion('extraInfo')" render=""></each>

        <div class="actions" if="getCartParam('possible_onepage_checkout')">
            <div class="primary">
                <button
                        id="top-cart-btn-checkout"
                        type="button"
                        class="action primary checkout"
                        data-action="close"
                        data-bind="
                            attr: {
                                title: $t('Proceed to Checkout')
                            },
                            click: closeMinicart()
                        "
                        translate="'Proceed to Checkout'">
                </button>
                <div data-bind="html: getCartParamUnsanitizedHtml('extra_actions')"></div>
            </div>
        </div>
    </if>

    <if args="getCartParam('summary_count')">
        <strong class="subtitle" translate="'Recently added item(s)'"></strong>
        <div data-action="scroll" class="minicart-items-wrapper">
            <ol id="mini-cart" class="minicart-items" data-bind="foreach: { data: getCartItems(), as: 'item' }">
                <each args="$parent.getRegion($parent.getItemRenderer(item.product_type))"
                      render="{name: getTemplate(), data: item, afterRender: function() {$parents[1].initSidebar()}}"></each>
            </ol>
        </div>
    </if>

    <ifnot args="getCartParam('summary_count')">
        <strong class="subtitle empty"
                translate="'You have no items in your shopping cart.'"></strong>
        <if args="getCartParam('cart_empty_message')">
            <p class="minicart empty text" text="getCartParam('cart_empty_message')"></p>
            <div class="actions">
                <div class="secondary">
                    <a class="action viewcart" data-bind="attr: {href: shoppingCartUrl}">
                        <span translate="'View and Edit Cart'"></span>
                    </a>
                </div>
            </div>
        </if>
    </ifnot>

    <div class="actions" if="getCartParam('summary_count')">
        <div class="secondary">
            <a class="action viewcart" data-bind="attr: {href: shoppingCartUrl}">
                <span translate="'View and Edit Cart'"></span>
            </a>
        </div>
    </div>

    <div id="minicart-widgets" class="minicart-widgets" if="regionHasElements('promotion')">
        <each args="getRegion('promotion')" render=""></each>
    </div>
    <div data-bind="scope:'sliding-cart'">
        <if args="isTotalsEnabled()">
    		<div id="totals" data-bind="scope:'totals'">
                <div class="totals-title" data-bind="text:getTotalsTitle()"></div>
                <div id="coupon" data-bind="scope:'coupon'">
                    <!-- ko template: getTemplate() --><!-- /ko -->
                </div>
    		    <!-- ko template: getTemplate() --><!-- /ko -->
    		</div>
        </if>
    </div>
</div>
<each args="getRegion('sign-in-popup')" render=""></each>

As per defined in view/frontend/layout/default.xml file to display upsell products, we will add app/code/Magelearn/AdvanceCart/view/frontend/web/template/minicart/total/grand-total.html file.

<!-- ko if: cart().summary_count && cart().summary_count > 0  -->
	<div class="upsell-data" data-bind="html: cart().advancecart.upsell_data">
	</div>
<!-- /ko -->

Now we will add JS component and html files for coupon and totals as per defined in view/frontend/layout/default.xml file.

Add app/code/Magelearn/AdvanceCart/view/frontend/web/template/coupon.html file.

<div data-bind="visible: isVisible()">
    <form class="cart-discount" id="slidingcart-coupon-form" method="post">
        <div role="alert" class="message message-error error" data-bind="visible: errorMsg">
            <div data-bind="text: errorMsg"></div>
        </div>
        <div role="alert" class="message message-success success" data-bind="visible: successMsg">
            <div data-bind="text: successMsg"></div>
        </div>
        <div class="fieldset coupon">
            <div class="field">
                <label for="slidingcart-coupon-code" class="label"></label>
                <div class="control">
                    <input class="input-text"
                           type="text"
                           id="slidingcart-coupon-code"
                           name="coupon_code"
                           data-bind="value: couponCode, attr: {disabled:isApplied(), placeholder: $t('Enter discount code')} "/>
                </div>
            </div>
            <div class="actions-toolbar">
                <div class="primary">
                    <!-- ko ifnot: isApplied() -->
                    <button class="action action-apply" type="button" data-bind="value: $t('Apply'), click: apply">
                        <span><!-- ko i18n: 'Apply'--><!-- /ko --></span>
                    </button>
                    <!-- /ko -->
                    <!-- ko if: isApplied() -->
                    <input type="hidden" name="remove" id="remove-coupon" value="1"/>
                    <button class="action action-cancel" type="button" data-bind="value: $t('Cancel'), click: cancel">
                        <span><!-- ko i18n: 'Cancel'--><!-- /ko --></span>
                    </button>
                    <!-- /ko -->
                </div>
            </div>
        </div>
    </form>
</div>

Add JS component file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/coupon.js

define([
    'ko',
    'jquery',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function (ko, $, Component, customerData, $t) {
    'use strict';

    var guestUrl    = 'guest-carts/:cartId/coupons/:couponCode',
        customerUrl = 'carts/mine/coupons/:couponCode';

    var cartData = customerData.get('cart')();

    return Component.extend({
        /**
        * @return {Boolean}
        */
        isVisible: function(){
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return ko.observable(Boolean(cartData.advancecart.coupon_enable));
            }
            return ko.observable(false);
        },
        couponCode: ko.observable(),
        isApplied: ko.observable(),
        quoteId: ko.observable(),
        isLoggedIn: ko.observable(),
        apiUrl: ko.observable(),
        errorMsg: ko.observable(),
        successMsg: ko.observable(),
        hideMsgTimeout: null,

        initialize: function () {
            this._super();
            this.initAdvanceCartData(customerData.get('cart')());
            return this;
        },

        initObservable: function () {
            var self = this;

            this._super();

            customerData.get('cart').subscribe(function (cartData) {
                self.initAdvanceCartData(cartData);
            });

            return this;
        },

        initAdvanceCartData: function (cartData) {
            if (cartData.hasOwnProperty('advancecart')) {
                this.couponCode(cartData.advancecart.coupon_code);
                this.isApplied(!!cartData.advancecart.coupon_code);
                this.isLoggedIn(cartData.advancecart.isLoggedIn);
                this.quoteId(cartData.advancecart.quoteId);
                this.apiUrl(cartData.advancecart.apiUrl);
            }
        },

        handleMsg: function (type) {
            $('#slidingcart-coupon-form .message-' + type).show();

            this.hideMsgTimeout = setTimeout(function () {
                $('#slidingcart-coupon-form .message-' + type).hide('blind', {}, 500);
            }, 3000);
        },

        apply: function () {
            var self  = this,
                field = $('#slidingcart-coupon-code');

            clearTimeout(this.hideMsgTimeout);

            if (!this.couponCode()) {
                field.focus().trigger('focusin');
                field.css('border-color', '#ed8380');

                return;
            }

            field.css('border-color', '');

            $.ajax({
                method: 'put',
                contentType: 'application/json',
                showLoader: true,
                url: this.buildUrl(this.quoteId(), this.couponCode()),
                data: JSON.stringify({
                    quoteId: this.quoteId(),
                    couponCode: this.couponCode()
                }),
                success: function () {
                    var cartData = customerData.get('cart')();

                    cartData.couponCode = self.couponCode();
                    customerData.set('cart', cartData);

                    self.handleSuccessApply();
                    self.handleMsg('success');
                },
                error: function (response) {
                    self.handleErrorResponse(response);
                    self.handleMsg('error');
                }
            });
        },

        cancel: function () {
            var self = this;

            clearTimeout(this.hideMsgTimeout);

            $.ajax({
                method: 'delete',
                contentType: 'application/json',
                showLoader: true,
                url: this.buildUrl(this.quoteId(), ''),
                data: JSON.stringify({quoteId: this.quoteId()}),
                success: function () {
                    var cartData = customerData.get('cart')();

                    cartData.couponCode = '';
                    customerData.set('cart', cartData);

                    self.handleSuccessCancel();
                    self.handleMsg('success');
                },
                error: function (response) {
                    self.handleErrorResponse(response);
                    self.handleMsg('error');
                }
            });
        },

        handleSuccessApply: function () {
            customerData.reload(['cart'], false);

            this.successMsg($t('Your coupon was successfully applied.'));
        },

        handleSuccessCancel: function () {
            customerData.reload(['cart'], false);

            this.successMsg($t('Your coupon was successfully removed.'));
        },

        handleErrorResponse: function (response) {
            this.errorMsg(response.responseJSON ? response.responseJSON.message : '');
        },

        buildUrl: function (cartId, couponCode) {
            var url = guestUrl;

            if (this.isLoggedIn()) {
                url = customerUrl;
            }

            return this.apiUrl() + url.replace(':cartId', cartId).replace(':couponCode', couponCode);
        }
    });
});

Now add total's html file at app/code/Magelearn/AdvanceCart/view/frontend/web/template/totals.html

<div data-bind="visible: isVisible()">
	<table>
	    <tbody>
	        <!-- ko foreach: getTotals() -->
	            <tr class="totals">
	                <th data-bind="i18n: title" class="mark" scope="row"></th>
	                <td class="amount">
	                    <span class="price" data-bind="text:value"></span>
	                </td>
	            </tr>
	        <!-- /ko -->
	    </tbody>
	</table>
</div>

Now we will add total's JS component file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/totals.js

define([
    'ko',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function(ko, Component, customerData, $t) {
    'use strict';

    var cartData = customerData.get('cart')();
    return Component.extend({
        /**
        * @return {Boolean}
        */
        isVisible: function(){
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return ko.observable(Boolean(cartData.advancecart.totals_enable));
            }
            return ko.observable(false);
        },
        /**
        * @return array
        */
        getTotals: function () {
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return cartData.advancecart.totals;
            }
            return {};
        },
        /**
        * @return {String}
        */
        getTotalsTitle: function () {
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return $t(cartData.advancecart.totals_title);
            }
            return '';
        }
    });
});

Now as per defined in view/frontend/layout/default.xml file, we will add sliding-cart.js compponent file.

Add app/code/Magelearn/AdvanceCart/view/frontend/web/js/sliding-cart.js

define([
    'ko',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function(ko, Component, customerData, $t) {
    'use strict';

    return Component.extend({
        /**
        * @return {Boolean}
        */
         isTotalsEnabled: function () {
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return ko.observable(Boolean(cartData.advancecart.totals_enable));
            }
            return ko.observable(false);
        }
    });
});
Now at last we will add grand-total.js component file to display totals data properly.
Add file at app/code/Magelearn/AdvanceCart/view/frontend/web/js/grand-total.js
define([
    'ko',
    'uiComponent',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function(ko, Component, customerData, $t) {
    'use strict';

    return Component.extend({
        /**
        * @return {String}
        */
         getGrandTotal: function () {
            if(Object.keys(customerData.get('cart')()).length > 0){
                var cartData = customerData.get('cart')();
                return cartData.advancecart.grand_total;
            }
            return '';
        }
    });
});
0 Comments On "Display coupon code, Upsell Products and totals information in Minicart Magneto2"

Back To Top