Add qty increment buttons to product page

Add qty increment buttons to product page

As you probably know, Magento is using a plain text field to handle quantity on product page. This is the bulletproof solution – it is simple and covers all needs. But, sometimes, merchants want to use something more appealing, and many of them settle for quantity increment buttons (those + and buttons next to the quantity input field). Adding this kind of behaviour will be the topic of this post.

Approaching this problem, or any other Magento related problem for that matter, few options present themselves. We could use good old inline javascript and handle everything from there. Since we’re using Magento 2, we could choose a route of jQuery widgets, and create one that would handle quantity increments. And, lastly, the thing we will use in this article are UI components with jQuery and Knockout.js.

If you need more information on Magento UI components, and using Knockout.js, take a look at this Inchoo post.

First thing we have to do is create an empty module. We’ll call it Inchoo_QtyIncrementors.
It will be located at /app/code/Inchoo/QtyIncrementors.
In order for Magento to see our module, we’ll need to create etc/module.xml and registration.php. Just put usual stuff in there (check core modules if you get stuck).
Since we’re changing product page functionality, we have to find the place where default quantity field is being rendered.
Let’s take a few (not so) wild guesses:

  1. it should be somewhere in core files
  2. it should be in the Catalog module
  3. it should be in frontend templates (view/frontend/templates directory)
  4. it is a product template, so it must be in product directory
  5. since it is not in product listing, it must be in a view directory (product/view)
  6. addtocart.phtml looks interesting, let’s check it out

(we now have /app/code/Magento/Catalog/view/frontend/template/catalog/product/view/addtocart.phtml)

And, after closer examination, there it is:

<div class="control">
    <input type="number"
           name="qty"
           id="qty"
           maxlength="12"
           value="<?php /* @escapeNotVerified */ echo $block->getProductDefaultQty() * 1 ?>"
           title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty"
           data-validate="<?php echo $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
           />
</div>

It is just a regular input field, as expected.
Let’s change it. Copy the file to your own module: /app/code/Inchoo/QtyIncrementors/view/frontend/templates/catalog/product/view/addtocart.phtml.

In order to create UI component, we need to initialize it. Add this somewhere below the input field.

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app": {
                "components": {
                    "qty_change": {
                        "component": "Inchoo_QtyIncrementors/js/view/product/view/qty_change",
                        "defaultQty": <?php echo $block->getProductDefaultQty() * 1 ?>
                    }
                 }
            }
        }
    }
</script>

We are telling Magento to create a component that can be found in the path mentioned (Inchoo_QtyIncrementors/js/view/product/view/qty_change – which translates to Inchoo/QtyIncrementors/view/frontend/web/js/view/product/view/qty_change.js), and also, we’re sending a parameter called 'defaultQty' which is rendered from server side.

In order to connect this component to HTML on the frontend, one more thing is needed. We have to bind it to some HTML element. Check this:

<div class="control" data-bind="scope: 'qty_change'">
 
    <button data-bind="click: decreaseQty">-</button>
 
    <input  data-bind="value: qty()"
            type="number"
            name="qty"
            id="qty"
            maxlength="12"
            title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty"
            data-validate="<?php echo $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
        />
 
    <button data-bind="click: increaseQty">+</button>
</div>

We’ve added a data-bind attribute to an outer div, as well as removed default value attribute on input field, and added a data-bind attribute of our own.
Think of the data-bind as a way of connecting HTML to a javascript function from our component. So, we’re binding the div and everything in it to our component (qty_change). What this means is that every variable or function call invoked there (knockout way, of course) will be searched for in our components code. This makes data-bind="value: qty()" clearer now: value of the input element is linked to a result of invoking qty() in our component.
There are two new buttons here as well, linked to our component via click event (data-bind="click: increaseQty"). Hopefully this makes sense, and if not, I promise you, it will in a minute.

Now, off to the last piece of the puzzle: component.

Located at /Inchoo/QtyIncrementors/view/frontend/web/js/view/product/view/qty_change.js, it has the following content:

define([
    'ko',
    'uiComponent'
], function (ko, Component) {
    'use strict';
 
    return Component.extend({
        initialize: function () {
            //initialize parent Component
            this._super();
            this.qty = ko.observable(this.defaultQty);
        },
 
        decreaseQty: function() {
            var newQty = this.qty() - 1;
            if (newQty < 1) {
                newQty = 1;
            }
            this.qty(newQty);
        },
 
        increaseQty: function() {
            var newQty = this.qty() + 1;
            this.qty(newQty);
        }
 
    });
});

Everything is much clearer now. In the initialize function, we have a qty variable (actually an observable, which is a knockout.js thing), that returns its value when invoked like a function (thus data-bind="value: qty()"). Also, there are two methods (decreaseQty and increaseQty) modifying this variable when button is clicked (remember the data-bind="click: increaseQty" ?).

And that is it.

If you try and refresh the page, nothing happens, because we forgot to change the default addtocart.phtml template, i.e. Magento does not know we want to use our own, so we have to change the template path in the layout. Let’s do it now. Create a new xml file /Inchoo/QtyIncrementors/view/frontend/layout/catalog_product_view.xml

Here is the file content:

<?xml version="1.0"?>
<page layout="1column" 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">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Inchoo_QtyIncrementors::catalog/product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
        <referenceBlock name="product.info.addtocart.additional">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Inchoo_QtyIncrementors::catalog/product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
    </body>
</page>

We’re referencing two blocks here – product.info.addtocart which is used for simple products, and product.info.addtocart.additional which is used for configurable ones. All we need to do is change the template – instead of using the default one, we use our own (from our module). This way, you can easily change templates for grouped and bundle products, as well. To implement qty increments for grouped or bundle products, you can use the same component, just make sure to have it instantiated for each input field.

Note: if you are overriding the files in your theme, it would be best to perform changes there, and not in the module.

And that’s it.

As always, we’ve prepared a module you can install via composer:

Add module as a dependency:

composer config repositories.inchoo_qtyincrementors vcs git@bitbucket.org:lurajcevi/inchoo_qtyincrementors.git

Ask Composer to download it:

composer require inchoo/qty_incrementors:dev-master

If Composer asks you for a set of credentials, please follow these instructions.

When composer does his thing, we can enable our module:

bin/magento module:enable Inchoo_QtyIncrementors

and later

bin/magento setup:upgrade

Our module has been enabled, and we can use it.

However, if this or anything else regarding Magento development confuses you, we will gladly check out your site and offer technical insights on what to improve based on our detailed custom report. Feel free to get in touch!

You made it all the way down here so you must have enjoyed this post! You may also like:

Declarative Schema feature in Magento 2 Josip Kovacevic
Josip Kovacevic, | 6

Declarative Schema feature in Magento 2

How to install Magento 2 Ivan Veres
Ivan Veres, | 23

How to install Magento 2

Enabling Multi-part MIME Emails in Magento Tomislav Nikcevski
Tomislav Nikcevski, | 3

Enabling Multi-part MIME Emails in Magento

19 comments

  1. Hi,
    I’ve added Quantity Increment/Decrement Buttons on Checkout Cart page inside ‘Magento_Checkout/templates/cart/item/default.phtml’ file. It is working properly when there is only one item in the cart. But, if there are multiple items in the cart, updating quantity for one item changes the quantity input field of all other items in the cart. Can you please help me how to fix this issue. Thank you!

  2. Hi, I’ve implemented this on list.phtml and is working but when updating quantity in one product, all other quantity inputs are updated too. Do you know how to update quantity specifically for the item that I has clicked the buttons? Thank you!

  3. Thanks for sharing this.

    Has the directory structure changed between the time this was written and v2.2?

    I’ve followed the structure outlined here (and in the bitbucket repo), but my module’s `addtocart.phtml` is not showing up.

    I feel like I’m missing something here.

    Any help appreciated!

  4. @Parth
    To add update on keypress in input field you need to add this function at thie end of qty_change.js:
    changeQty: function () {
    var newQty = parseInt(document.getElementById(‘qty’).value);
    this.qty(newQty);
    }
    and then in input data-bind add this:

    1. To add update on keypress in input field you need to add this function at thie end of qty_change.js:
      changeQty: function () {
      var newQty = parseInt(document.getElementById(‘qty’).value);
      this.qty(newQty);
      }
      and then in input data-bind add this:
      <input data-bind="value: qty(), event: {keyup: changeQty}"

  5. If I am changing quantity manually into input box and after that click on + / – button then quantity incremented with considering previous value as current value.
    Example : Right now there is value “1” in input box and I will change it to “100” using manually add in input box and after that I click on (+) button then it should be “101” quantity but display 2 because It is taking previous value.

    1. As the qty_change.js is not including, so I explicitly included it but still it doesn’t worked for me.
      I tried to download module from composer but its not downloading.

  6. Hi,

    I tried adding the buttons on my bundle product using the radio button as the type.
    I managed to do so by editing the vendor/magento/module-bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml template, but when I click on the increase or decrease button on one product, all my product quantities are changed.

    Any idea how to fix this?

  7. Don’t know why this feature isn’t added directly into the default Magento package. Any idea why? Because almost everyone needs this feature even in Magento 1.x or Magento 2.x

  8. I successfully installed the module, but I don’t see any + or – button to increase/decrease quantity.
    It seems the module doesn’t get executed.
    I tried to add a Hello inside app/code/Inchoo/QtyIncrementors/view/frontend/templates/catalog/product/view/addtocart.phtml and doesn’t show.

    Magento 2.0.5, developer mode.

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.