Magento 2:- Debugging and Customization of Mini Cart

I found lots of developer struggling out with customization of minicart in Magento 2, so thought to write this. Magento2 has modified things lots to render minicart using knockout js, template and layout xml. We will be going through debugging process step by step to understand more, drop in comments if you have any issue with any part.

Below you can see the image for minicart in magento 2:-

If you inspect the html code you will land up with something like this:-

From where does that “minicart-wrapper” comes, here we found it

vendor\magento\module-checkout\view\frontend\templates\cart.phtml

Let’s review layout file which is responsible to call this template:-

vendor\magento\module-checkout\view\frontend\layout\default.xml

<referenceContainer name="header-wrapper">
    <block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">
        <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">Magento_Checkout/minicart/content</item>
                            </item>
                            <item name="children" xsi:type="array">
                                <item name="subtotal.container" xsi:type="array">
                                    <item name="component" xsi:type="string">uiComponent</item>
                                    <item name="config" xsi:type="array">
                                        <item name="displayArea" xsi:type="string">subtotalContainer</item>
                                    </item>
                                    <item name="children" xsi:type="array">
                                        <item name="subtotal" xsi:type="array">
                                            <item name="component" xsi:type="string">uiComponent</item>
                                            <item name="config" xsi:type="array">
                                               <item name="template" xsi:type="string">Magento_Checkout/minicart/subtotal</item>
                                            </item>
                                        </item>
                                   </item>
                                </item>
                                <item name="extra_info" xsi:type="array">
                                    <item name="component" xsi:type="string">uiComponent</item>
                                        <item name="config" xsi:type="array">
                                            <item name="displayArea" xsi:type="string">extraInfo</item>
                                        </item>
                                    </item>
                                    <item name="promotion" xsi:type="array">
                                        <item name="component" xsi:type="string">uiComponent</item>
                                        <item name="config" xsi:type="array">
                                            <item name="displayArea" xsi:type="string">promotion</item>
                                    </item>
                               </item>
                        </item>
                    </item>
                </item>
            </argument>
        </arguments>
        <container name="minicart.addons" label="Mini-cart promotion block"/>
    </block>
</referenceContainer>

We will be going through this layout xml step by step, lets understand below line


<block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">

So above line add a block with block class as Magento\Checkout\Block\Cart\Sidebar.php and template file path as

 Magento-Checkout/view/frontend/templates/cart/minicart.phtml

Let’s look at our template file minicart.phtml code to find “minicart-content-wrapper”class


<div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

So we got our class, now check the statement getTemplate using knockout JS. From where does our template is now coming????? Oh, its time to go back and review default.xml once again and get the block components details(check for below code).


<item name="config" xsi:type="array">
    <item name="template" xsi:type="string">Magento_Checkout/minicart/content</item>
</item>

Above code provides the template details:-

Magento_Checkout/View/frontend/web/template/minicart/content.html

You can find your sidebar template here. We will be going in depth with items coming in sidebar (rest code will become quite easy for you to understand once we will cover items part). Check below code for this:-

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

Above code has ordered list to show item recursively and data is being fetched using foreach loop of knockout.js

foreach: { data: getCartParam('items'), as: 'item' }

From where can I get this data in foreach, this question might be coming to your mind…?.. Lets discover it; where can it be? The answer can be found in default.xml, check below line of code:-

Magento_Checkout/js/view/minicart

So here we got the path of JS from where we can check about data..
Path to js file:-

Magento_Checkout/view/frontend/web/js/view/minicart.js

As you can see we have a function getCartParam(‘items’) in foreach, this function can be found in line 150,

	getCartParam: function (name) {
            if (!_.isUndefined(name)) {
                if (!this.cart.hasOwnProperty(name)) {
                    this.cart[name] = ko.observable();
                }
            }

            return this.cart[name]();
        }

This function gets the value from cart variable, which is getting its value set in update function

update: function (updatedCart) {
            _.each(updatedCart, function (value, key) {
                if (!this.cart.hasOwnProperty(key)) {
                    this.cart[key] = ko.observable();
                }
                this.cart[key](value);
            }, this);
        },

Update function is called in initialize(), and passing cartData() as a parameter, cartData is getting its value from customerData.get(‘cart’) i.e, we now need to check for customerData object get() function.

initialize: function () {
            var self = this,
                cartData = customerData.get('cart');
            this.update(cartData());
……..

CustomerData is being set using ‘Magento_Customer/js/customer-data’. Before we proceed a quick idea about define function. This function is from requireJs – first parameter of “define” function is array of dependency and second parameter is definition of our function. Definition function should always return an object. So now lets look at

Magento_Customer/view/frontend/web/js/customer-data.js

This file is responsible for sending ajax request to get data. Lets traverse through its code one by one. As mentioned above we have to reach customerData.get() , check below code in this file for the same:-

get: function (sectionName) {
            return buffer.get(sectionName);
        },

Now as we can see it is calling buffer.get(), so lets move to that code:-

get: function (sectionName) { 
            if (!this.data[sectionName]) {
                this.bind(sectionName);
            }

            return this.data[sectionName];
        },

Here code is calling bind() function which is setting value in data model:-

bind: function (sectionName) {
            this.data[sectionName] = ko.observable({});
        },

Now if you check customer.init() function there is a check for needReload(), which tells if we need reload or data should come from cache directly. Here we are either calling this.reload() if reload is needed or we are getting data from cache dataProvider.getFromStorage(), no need to reload here if keys validated properly.

Let check the reload function which is calling dataProvider.getFromServer() function and that is sending ajax request to get customer data:-

reload: function (sectionNames, updateSectionId) {
            return dataProvider.getFromServer(sectionNames, updateSectionId).done(function (sections) {
                buffer.update(sections);
            });
        },

Now check the getfromServer(), here you can see an ajax call.

            return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) {
                throw new Error(jqXHR);
            });

You must be thinking we have jumped from this.data[sectionName] = ko.observable({}); directly to customerData.init(). As we can see we are observing the data value here, so we understand the code which is responsible for providing changed value to this variable. There is one more code responsible for fetching data i.e, calling reload function

$(document).on('ajaxComplete', function (event, xhr, settings) {
        var sections,
            redirects;

        if (settings.type.match(/post|put/i)) {
            sections = sectionConfig.getAffectedSections(settings.url);

            if (sections) {
                customerData.invalidate(sections);
                redirects = ['redirect', 'backUrl'];

                if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) {
                    return;
                }
                customerData.reload(sections, true);
            }
        }
    });

Enough of this, now lets get back to our content.html, so far we understand from where data is coming in foreach, now look at the inner foreach:-

foreach: $parent.getRegion($parent.getItemRenderer(item.product_type))

item.product_type => coming from loop
$parent.getItemRenderer => it means getItemRenderer() function of parent js file i.e, minicart.js

If you do console.log(($parent.getItemRenderer(item.product_type)), then you will get the name of displayArea to be shown. Like:- defaultRenderer

Now look into checkout_cart_sidebar_item_renderers.xml, here we have code of our block ‘minicart’, we have our region as seen in below code:-


<item name="itemRenderer" xsi:type="array">
    <item name="default" xsi:type="string">defaultRenderer</item>
    <item name="simple" xsi:type="string">defaultRenderer</item>
    <item name="virtual" xsi:type="string">defaultRenderer</item>
</item>

Just check below line to get the template of items of minicart.


<item name="displayArea" xsi:type="string">defaultRenderer</item>
<item name="template" xsi:type="string">Magento_Checkout/minicart/item/default</item>

So, we got our template file Magento_Checkout/view/frontend/web/template/minicart/item/default.html

I think that is enough for now and will help you a lot to debug and understand the code flow.

Leave a Comment.