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

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

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.

quantity increment decrement

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!

Previous Article

How to make Sidebar Sticky in Magento 2

Next Article

How To Create A Unit Test File In Magento 2

Write a Comment
  1. 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

      1. Hi, i too faced similar issue, the “items in cart” remains the same even if increment the quantity

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

Leave a Comment

Your email address will not be published. Required fields are marked *

Get Connect With Us

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨