How To

Magento 2: How to Add Quantity Increment & Decrement Functionality at Checkout Summary

Hello Magento Friends,

Hope all are healthy and safe. Today I am here with a significant solution that will make your Magento store feature-rich. Magento 2: How to Add Quantity Increment & Decrement Functionality at Checkout Summary.

E-commerce store owners are always finding ways for offering convenience and comfort to customer’s shopping journey. If the customers need to navigate more pages to perform any action, it results in a bad user experience of the store. One such case is increasing and decreasing product quantity.

Customers can only increase or decrease the product quantity on the cart or mini cart. But sometimes we need to add this functionality to the checkout summary page. The customers can easily perform increment and decrement of product quantity or even remove the product from the checkout page without moving to the cart page or mini cart.

Let’s find out the steps to Add Quantity Increment & Decrement Functionality at Checkout Summary in Magento 2.

Steps to Add Quantity Increment & Decrement Functionality at Checkout Summary in Magento 2:

Step 1:  First you need to add /modify the details.html file in the following path

app\design\frontend\Themes\Yourtheme\Magento_Checkout\web\template\summary\item\details.html

<!-- ko foreach: getRegion('before_details') -->
    <!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->
<div class="product-item-details">

    <div class="product-item-inner">
        <div class="product-item-name-block">
            <strong class="product-item-name" data-bind="html: $parent.name">
            </strong>
            <div class="details-qty qty">
                <label class="label" data-bind="i18n: 'Qty', attr: {
                           for: 'cart-item-'+$parent.item_id+'-qty'}" style="display: none;">
                </label>
                <button data-bind="attr: {
                           id: 'minus-cart-item-'+$parent.item_id,
                           'data-cart-item': $parent.item_id,
                           'data-btn-minus': 'minus',
                           },click:updateItemQtyCheckout"
                        class="update-cart-item minus">
                        -
                </button>
                <input data-bind="attr: {
                           id: 'cart-item-'+$parent.item_id+'-qty',
                           'data-cart-item': $parent.item_id,
                           'data-item-qty': $parent.qty,
                           'data-cart-item-id': $parent.product_sku
                           }, value: $parent.qty"
                       type="number"
                       size="4"
                       class="item-qty cart-item-qty" readonly>

                <button data-bind="attr: {
                           id: 'plus-cart-item-'+$parent.item_id,
                           'data-cart-item': $parent.item_id,
                           'data-btn-plus': 'plus'
                           },click:updateItemQtyCheckout"
                        class="update-cart-item plus">
                    <!--<span data-bind="i18n: '+'"></span>-->
                    +
                </button>
                <button data-bind="attr: {
                           id: 'update-cart-item-'+$parent.item_id,
                           'data-cart-item': $parent.item_id,
                           title: $t('Update')
                           }"
                        class="update-cart-item"
                        style="display: none">
                    <span data-bind="i18n: 'Update'">
                    </span>
                </button>
            </div>
        </div>
        <!-- ko foreach: getRegion('after_details') -->
            <!-- ko template: getTemplate() --><!-- /ko -->
        <!-- /ko -->
    </div>

    <!-- ko if: (JSON.parse($parent.options).length > 0)-->
    <div class="product options" data-bind="mageInit: {'collapsible':{'openedState': 'active'}}">
        <span data-role="title" class="toggle"><!-- ko i18n: 'View Details' --><!-- /ko -->
        </span>
        <div data-role="content" class="content">
            <strong class="subtitle"><!-- ko i18n: 'Options Details' --><!-- /ko -->
            </strong>
            <dl class="item-options">
                <!--ko foreach: JSON.parse($parent.options)-->
                <dt class="label" data-bind="text: label">
                </dt>
                    <!-- ko if: ($data.full_view)-->
                    <dd class="values" data-bind="html: full_view">
                    </dd>
                    <!-- /ko -->
                    <!-- ko ifnot: ($data.full_view)-->
                    <dd class="values" data-bind="html: value">
                    </dd>
                    <!-- /ko -->
                <!-- /ko -->
            </dl>
        </div>
    </div>
    <!-- /ko -->
</div>
<!-- ko foreach: getRegion('item_message') -->
    <!-- ko template: getTemplate() --><!-- /ko -->
<!-- /ko -->

Step 2:  Now add/modify the details.js file in the following path

app\design\frontend\Themes\Yourtheme\Magento_Checkout\web\js\view\summary\item\details.js

define([
    'jquery',
    'uiComponent',
    'Magento_Customer/js/model/authentication-popup',
    'Magento_Customer/js/customer-data',
    'Magento_Checkout/js/model/quote',
    'Magento_Checkout/js/action/get-totals',
    'Magento_Checkout/js/model/shipping-service',
    'Magento_Checkout/js/model/shipping-rate-registry',
    'Magento_Checkout/js/model/resource-url-manager',
    'mage/storage',
    'Magento_Checkout/js/model/error-processor',
    'mage/url',
    'Magento_Ui/js/modal/alert',
    'Magento_Ui/js/modal/confirm',
    'underscore',
    'jquery/ui',
    'mage/decorate',
    'mage/collapsible',
    'mage/cookies'
], function ($, Component, authenticationPopup, customerData, quote, getTotalsAction, shippingService, rateRegistry, resourceUrlManager, storage, errorProcessor, url,alert, confirm, _) {
    'use strict';

    return Component.extend({
        shoppingCartUrl: window.checkout.shoppingCartUrl,
        defaults: {
            template: 'Magento_Checkout/summary/item/details'
        },

        /**
         * @param {Object} quoteItem
         * @return {String}
         */        getValue: function (quoteItem)
        {
            return quoteItem.name;
        },
        updateItemQtyCheckout: function (data, event)
        {
            var btnminus = "";
            var btnplus = "";
            if (event.target.classList[1] == "minus")
            {
                btnminus = event.currentTarget.dataset.btnMinus;
            }
            if (event.target.classList[1] == "plus")
            {
                btnplus = event.currentTarget.dataset.btnPlus;
            }
            var itemId = event.currentTarget.dataset.cartItem;


            // If element is minus and quantity is '1' than remove
            var elem = $('#cart-item-' + itemId + '-qty');
            if(event.target.classList[1] == 'plus')
            {
                 elem.val(parseInt(elem.val()) + 1)
            }
            else if(event.target.classList[1] == 'minus')
            {
                elem.val(parseInt(elem.val()) - 1)
            }
                

            if (event.target.classList[1] == "minus" && $('#cart-item-' + itemId + '-qty').val() == '0')
            {
                var productData = this._getProductById(Number(itemId));

                if (!_.isUndefined(productData))
                {
                    var self = this;
                    var elemr = elem;
                    self._ajax(url.build('checkout/sidebar/removeItem'), {
                                    'item_id': itemId
                                }, elemr, self._removeItemAfter);

                    if (window.location.href === self.shoppingCartUrl)
                    {
                          window.location.reload(false);
                    }
                    
                }
            }
            else
            {
                this._ajax(url.build('checkout/sidebar/updateItemQty'), {
                    'item_id': itemId,
                    'item_qty': $('#cart-item-' + itemId + '-qty').val(),
                    'item_btn_plus': btnplus,
                    'item_btn_minus': btnminus
                }, elem, this._updateItemQtyAfter);
            }
        },
        _getProductById: function (productId)
        {
            return _.find(customerData.get('cart')().items, function (item)
            {
                return productId === Number(item['item_id']);
            });
        },
        _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);
            this._customerData();
        },
        _customerData: function ()
        {
            var deferred = $.Deferred();
            getTotalsAction([], deferred);
            var sections = ['cart'];
            customerData.invalidate(sections);
            customerData.reload(sections, true);
            var self = this;
            self._estimateTotalsAndUpdateRatesCheckout();
        },
        _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));
                });
        },
        _hideItemButton: function (elem)
        {
            var itemId = elem.data('cart-item');

            $('#update-cart-item-' + itemId).hide('fade', 300);
        },
        _removeItemAfter: function (elem)
        {
            var productData = this._getProductById(Number(elem.data('cart-item')));

            if (!_.isUndefined(productData))
            {
                $(document).trigger('ajax:removeFromCart', {
                    productIds: [productData['product_id']]
                });
                var sections = ['cart'];

                setTimeout(function ()
                {
                    if (customerData.get('cart')().items.length == 0)
                    {
                        window.location.reload();
                    }
                }, 2000);

                if (window.location.href.indexOf(this.shoppingCartUrl) === 0)
                {
                    window.location.reload();
                }
            }
            this._customerData();
        },
        _estimateTotalsAndUpdateRatesCheckout: function ()
        {
            var serviceUrl, payload;
            var address = quote.shippingAddress();
            shippingService.isLoading(true);
            serviceUrl = resourceUrlManager.getUrlForEstimationShippingMethodsForNewAddress(quote);
            payload = JSON.stringify({
                    address: {
                        'street': address.street,
                        'city': address.city,
                        'region_id': address.regionId,
                        'region': address.region,
                        'country_id': address.countryId,
                        'postcode': address.postcode,
                        'email': address.email,
                        'customer_id': address.customerId,
                        'firstname': address.firstname,
                        'lastname': address.lastname,
                        'middlename': address.middlename,
                        'prefix': address.prefix,
                        'suffix': address.suffix,
                        'vat_id': address.vatId,
                        'company': address.company,
                        'telephone': address.telephone,
                        'fax': address.fax,
                        'custom_attributes': address.customAttributes,
                        'save_in_address_book': address.saveInAddressBook
                    }
                }
            );
            storage.post(
                serviceUrl, payload, false
            ).done(function (result) {
                rateRegistry.set(address.getCacheKey(), result);
                shippingService.setShippingRates(result);
            }).fail(function (response) {
                shippingService.setShippingRates([]);
                errorProcessor.process(response);
            }).always(function () {
                shippingService.isLoading(false);
            });
        }
    });
});

After implementing the above code the quantity increment & decrement functionality will be added to the checkout summary of your Magento 2 store.

Conclusion:

Hopefully, everyone is able to Add Quantity Increment & Decrement Functionality at Checkout Summary in Magento 2. Besides this, you can also Add Discount Component to Checkout Order Summary in Magento 2.

If you have trouble executing the code, feel free to reach me via comment. I will be glad to help you out. Do share the article on your social media and stay updated for more tutorials.

Happy Coding!

Click to rate this post!
[Total: 6 Average: 5]
Dhiren Vasoya

Dhiren Vasoya is a Director and Co-founder at MageComp, Passionate 🎖️ Certified Magento Developer👨‍💻. He has more than 9 years of experience in Magento Development and completed 850+ projects to solve the most important E-commerce challenges. He is fond❤️ of coding and if he is not busy developing then you can find him at the cricket ground, hitting boundaries.🏏

View Comments

  • Hi, what is the use of _estimateTotalsAndUpdateRatesCheckout function in .js file? doesn't the core file already have this function to estimate rate?

  • Hi Dhiren,
    This is a very helpful Code snipped. Thank you very much!

    Unfortunately counter of "items in cart" (below order summary) will not refresh after changing the quantity.
    Do you have an idea?

    Best regards,
    Daniel

    • We haven't face such issues. Try to check the console or Magento Log File so you can get an idea about this one.

Recent Posts

What are Net Sales? How to Calculate Your Net Sales?

In the world of business, understanding financial metrics is crucial for making informed decisions and…

2 days ago

Magento 2 Extensions Digest April 2024 (New Release & Updates)

Welcome to the MageComp Monthly Digest, where we bring you the latest updates, releases, and…

2 days ago

The ABCs of Geofencing: Definition, Features and Uses

In this era, businesses are always on the lookout for ways to engage with their…

3 days ago

How to Delete Product Variant in a Shopify Remix App using GraphQL Mutations?

Managing a Shopify store efficiently involves keeping your product catalog organized. This includes removing outdated…

4 days ago

6 Innovative Tools Revolutionizing E-Commerce Operations

E-commerce has transformed the way consumers shop for products and services and interact with businesses.…

6 days ago

How Upcoming Cookie Changes Will Affect Your E-commerce Website?

The e-commerce world is constantly in flux. New tech and strategies emerge daily to help…

6 days ago