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:
- it should be somewhere in core files
- it should be in the Catalog module
- it should be in frontend templates (view/frontend/templates directory)
- it is a product template, so it must be in product directory
- since it is not in product listing, it must be in a view directory (product/view)
- 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!
19 comments
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!
Changes all product quantities for grouped and bundle products. Any idea on how to fix this?
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!
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!
@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:
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}"
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.
how to add same for grouped products
i have take clone from git added module but increment button is not working
in magento 2 how to make qty as dropdown in product page for group products
Did someone have implemented qty increment on the shopping cart items?
Do I need to include qty_change.js file in the head tag?
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.
cat this be added to grouped products as well ?
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?
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
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.
Would love to see a A/B test on empty boxes vs qty buttons..
I have created a store in Magento 2 EE, but was eager to tutorials Magento 2 by Inchoo! Thanks 😀