This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

📣 Join our live AMA on the Future of Page Builders with Brizy's CEO! Register Now →

The Basic Concepts of Magento 2 Knockout JS

Updated on March 16, 2023

5 Min Read

Knockout is a Javascript library which helps in the frontend of Magento 2. It implements MVVM (Model-View-View-Model) design pattern. You can find Knockout JS in Magento 2 on almost every page, but mostly on the checkout page. The implementation of Magento 2 Knockout JS is a bit tricky.

The goal of this post is to explain the basic concepts of Magento 2 Knockout JS which are valid to use in Magento 2 and we will implement very simple logic as well. If you are not familiar with Knockout Javascript library, I would like you to read the documentation of Knockout JS.

Magento 2 is using a text field to handle quantity on the product page. But if you want quantity increment buttons, you can easily add this kind of behavior by using Knockout JS in Magento 2.

Scale Your Magento 2 Store With Ease

One-Click Magento installation with your own managed hosting solution.

First of all, create a Magento 2 module. In our example, all files will be located in Cloudways_Mymodule module. Location of our module is MAGENTO2_ROOT > app > code > Cloudways > Mymodule. Now, create a registration.php in app > code > Cloudways > Mymodule

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Cloudways_Mymodule',
    __DIR__
);

and module.xml in app > code > Cloudways > Mymodule > etc

<?xml version="1.0"?>
	<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
	<module name="Cloudways_Mymodule" setup_version="1.0.0"></module>
</config>

so Magento 2 can see our module. As you know, we are going to make changes in the behavior of quantity, so we have to find the place from where Magento 2 is rendering the default quantity field on the product page. After some findings, we have got the following template which can help us.

Magento > Catalog > view > frontend > templates > catalog > product > view > addtocart.phtml

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

/** @var $block \Magento\Catalog\Block\Product\View */
?>
<?php $_product = $block->getProduct(); ?>
<?php $buttonTitle = __('Add to Cart'); ?>
<?php if ($_product->isSaleable()): ?>
<div class="box-tocart">
    <div class="fieldset">
        <?php if ($block->shouldRenderQuantity()): ?>
        <div class="field qty">
            <label class="label" for="qty"><span><?php /* @escapeNotVerified */ echo __('Qty') ?></span></label>
            <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>
        </div>
        <?php endif; ?>
        <div class="actions">
            <button type="submit"
                    title="<?php /* @escapeNotVerified */ echo $buttonTitle ?>"
                    class="action primary tocart"
                    id="product-addtocart-button">
                <span><?php /* @escapeNotVerified */ echo $buttonTitle ?></span>
            </button>
            <?php echo $block->getChildHtml('', true) ?>
        </div>
    </div>
</div>
<?php endif; ?>
<?php if ($block->isRedirectToCartEnabled()) : ?>
<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "Magento_Catalog/product/view/validation": {
                "radioCheckboxClosest": ".nested"
            }
        }
    }
</script>
<?php else : ?>
<script>
    require([
        'jquery',
        'mage/mage',
        'Magento_Catalog/product/view/validation',
        'Magento_Catalog/js/catalog-add-to-cart'
    ], function ($) {
        'use strict';

        $('#product_addtocart_form').mage('validation', {
            radioCheckboxClosest: '.nested',
            submitHandler: function (form) {
                var widget = $(form).catalogAddToCart({
                    bindSubmit: false
                });

                widget.catalogAddToCart('submitForm', $(form));

                return false;
            }
        });
    });
</script>
<?php endif; ?>

Copy the addtocart.phtml file to your own module:

app > code > Cloudways > Mymodule > view > frontend > template > catalog > product > view > addtocart.phtml

Magento 2 Knockout JS has a dependency of UI Component which further inherits classes and methods from UI Element. In our addtocart.phtml, we will be creating a UI component and initialize it. We will be telling Magento 2 to create a component that will be located in:

app > code > Cloudways > Mymodule > view > frontend > web > js > view > product > view > qty_change.js

Add the below script somewhere above the input field of quantity:

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

Since we have created our component named as qty_change, we need to connect/bind it with the front end HTML, like 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 echo __('Qty') ?>"
    class="input-text qty"
    data-validate="<?php echo $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
    />
    <button data-bind="click: increaseQty">+</button>
</div>

In the above code, we have a data-bind attribute to the div, as well as in the input field. The data-bind attribute is used as a medium to connect HTML with a Javascript function of our component qty_change. It means according to the Knockout way; every function call invoked there will be searched in our qty_change component.

This leads you to the understanding that the value of the input field is linked to a result of invoking qty() function located in the component. Also, there are two buttons as well, connected to the component via Javascript click event. They will help us decrease/increase in quantity value. So, the final view of our addtocart.phtml will be:

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

/** @var $block \Magento\Catalog\Block\Product\View */
?>
<?php $_product = $block->getProduct(); ?>
<?php $buttonTitle = __('Add to Cart'); ?>
<?php if ($_product->isSaleable()): ?>
<div class="box-tocart">
    <div class="fieldset">
        <?php if ($block->shouldRenderQuantity()): ?>
        <div class="field qty">
            <label class="label" for="qty"><span><?php /* @escapeNotVerified */ echo __('Qty') ?></span></label>
            <script type="text/x-magento-init">
            {
                "*": {
                        "Magento_Ui/js/core/app": {
                            "components": {
                                "qty_change": {
                                    "component": "Cloudways_Mymodule/js/view/product/view/qty_change",
                                    "defaultQty": <?php echo $block->getProductDefaultQty() * 1 ?>
                                }
                            }
                        }
                }
            }
            </script>
            <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>
        </div>
        <div>
            <input
                type="text"
                name="remarks"
                id="remarks"
                maxlength="255"
                placeholder="Remarks"
            />
        </div>
        <br>
        <?php endif; ?>
        <div class="actions">
            <button type="submit"
                    title="<?php /* @escapeNotVerified */ echo $buttonTitle ?>"
                    class="action primary tocart"
                    id="product-addtocart-button">
                <span><?php /* @escapeNotVerified */ echo $buttonTitle ?></span>
            </button>
            <?php echo $block->getChildHtml('', true) ?>
        </div>
    </div>
</div>
<?php endif; ?>
<?php if ($block->isRedirectToCartEnabled()) : ?>
<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "Magento_Catalog/product/view/validation": {
                "radioCheckboxClosest": ".nested"
            }
        }
    }
</script>
<?php else : ?>
<script>
    require([
        'jquery',
        'mage/mage',
        'Magento_Catalog/product/view/validation',
        'Magento_Catalog/js/catalog-add-to-cart'
    ], function ($) {
        'use strict';

        $('#product_addtocart_form').mage('validation', {
            radioCheckboxClosest: '.nested',
            submitHandler: function (form) {
                var widget = $(form).catalogAddToCart({
                    bindSubmit: false
                });

                widget.catalogAddToCart('submitForm', $(form));

                return false;
            }
        });
    });
</script>
<?php endif; ?>

Now, let’s talk about the last stage: qty_change component.

Create a new file qty_change.js in app > code > Cloudways > Mymodule > view > frontend > web > js > view > product > view and add the following content in it.

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);
        }
 
    });
});

In the above code, everything is clear enough now. Look at the initialize: function (). We have initialized a qty observable, a Magento 2 Knockout JS thing, that returns its value when invoked by data-bind attribute from HTML. There are also two more functions decreaseQty and increaseQty. They help to modify the value when the button from HTML is clicked.

And that’s all. Now, the last thing is to change the default addtocart.phtml template. Magento 2 doesn’t know that we want to use our addtocart.phtml file, so we have to modify the template path using the layout. Let’s do it now. Create a new file catalog_product_view.xml in app > code > Cloudways > Mymodule > view > frontend > layout and add the following code:

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

Finally, we had prepared our module using Knockout JS in Magento 2. Please enable and activate your module using below Magento 2 CLI commands:

rm -rf var/di var/generation var/cache/* var/log/* var/page_cache/*
php bin/magento module:enable Cloudways_Mymodule
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento indexer:reindex
php bin/magento cache:clean
php bin/magento cache:flush

Conclusion

Our module is ready to go, and we can also use it on a regular basis. Magento 2 Knockout JS helps to build some parts of Magento 2 frontend dynamically. I hope you have understood this simple example of Magento 2 Knockout JS, and it will be helpful in your Magento 2 development. If you have anything to discuss related to it, feel free to share your thoughts in the comments section.

Where is Knockout JS used in Magento 2?

Knockout JS is a java script library used in Magento 2 Front end. It is used in almost every Magento 2 page and is mostly present on checkout page.

How Knockout JS works in Magento 2?

Knockout JS works on Model-View-View-Model (MVVM) patter and is used for dynamically building of certain Magento 2 pages like Checkout page.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Abdur Rahman

Abdur Rahman is the Magento whizz at Cloudways. He is growth ambitious, and aims to learn & share information about Ecommerce & Magento Development through practice and experimentation. He loves to travel and explore new ideas whenever he finds time. Get in touch with him at [email protected]

×

Get Our Newsletter
Be the first to get the latest updates and tutorials.

Thankyou for Subscribing Us!

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour

CYBER WEEK SAVINGS

  • 0

    Days

  • 0

    Hours

  • 0

    Mints

  • 0

    Sec

GET OFFER

For 4 Months &
40 Free Migrations

For 4 Months &
40 Free Migrations

Upgrade Now