Cookies setting

Cookies help us enhance your experience on our site by storing information about your preferences and interactions. You can customize your cookie settings by choosing which cookies to allow. Please note that disabling certain cookies might impact the functionality and features of our services, such as personalized content and suggestions. Cookie Policy

Cookie Policy
Essential cookies

These cookies are strictly necessary for the site to work and may not be disabled.

Information
Always enabled
Advertising cookies

Advertising cookies deliver ads relevant to your interests, limit ad frequency, and measure ad effectiveness.

Information
Analytics cookies

Analytics cookies collect information and report website usage statistics without personally identifying individual visitors to Google.

Information
mageplaza.com

An instruction to utilize Knockout JS in Magento 2

Vinh Jacker | 11-11-2024

An instruction to utilize Knockout JS in Magento 2

Knockout is a widely-used JavaScript library in the user interface of Magento 2, employing the MVVM (Model-View-View-Model) design pattern. It is present on various pages of Magento 2, but primarily on the checkout page. However, integrating Knockout JS into Magento 2 can be somewhat complex.

The purpose of this article is to explain the basic principles of Knockout JS in Magento 2, focusing on concepts applicable to Magento 2 and presenting how to implement simple logic.

Introduction about Knockout JS

The JavaScript library known as Knockout JS is essentially used to build specific elements of the Magento 2 user interface in a flexible way. The Model-View-View-Model (MVVM) pattern is used, which in Magento 2 can be somewhat confusing and challenging to apply in practice. However, it significantly improves the smooth operation of an online store built on Magento 2.

Furthermore, the development process facilitated by Knockout JS is incredibly powerful and engaging. In short, Magento primarily relies on the data binding concept of Knockout JS, which is used in many crucial components, including the checkout and mini cart.

Why should we use Data Binding in the first place?

“Can we use basic libraries like JQuery to manually parse the DOM?” - Absolutely!

To be honest, JQuery isn’t really necessary for this purpose. All DOM operations can be performed using simple JavaScript. But in Magento 2, Knockout JS offers benefits such as standardized, clean, and maintainable code. It also improves the speed and security of Magento 2. Therefore, we will guide you on using Knockout JS and building it into a custom module in detail after this article.

How to use Knockout JS in Magento 2

To get started, create a Magento 2 module named Mageplaza_Mymodule. The path MAGENTO2_ROOT > app > code > Mageplaza > Mymodule is where you can find our module. Now, create the register.php file by navigating to app > code > Mageplaza > Mymodule.

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

and in module.xml in app > code > Mageplaza > 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="Mageplaza_Mymodule" setup_version="1.0.0"></module>
</config>

We need to identify where Magento 2 displays the default quantity field on the product page because, as you know, we will be changing the behavior of the quantity. We have the following template that might be helpful after some research.

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 file addtocart.phtml into the module you created: app > code > Mageplaza > Mymodule > view > frontend > templates > catalog > product > view > addtocart.phtml

The UI component, which inherits additional classes and methods from the UI component, is a dependency of Magento 2 Knockout JS. We will build and initialize the UI component in our addtocart.phtml file. We will instruct Magento 2 to create a component in: app > code > Mageplaza > Mymodule > view > frontend > web > js > catalog > product > view > qty_change.js

Insert the following code above the quantity input field:

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

Now that our qty_change component has been built, we need to link or connect it with the frontend HTML as follows:


<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>

The above code uses the data-bind attribute in both the div and input fields. This attribute acts as a bridge between the HTML and the JavaScript functionality of our qty_change component. Essentially, in the Knockout framework, any function call referenced here will be looked for in our qty_change component.

This helps you realize that the value entered into the input field is related to the result of using the qty() function of the component. Additionally, there are two buttons linked to the component using JavaScript click events. They will help us change the quantity value. Therefore, the final view of 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": "Mageplaza_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 discuss the final step, the qty_change component.

Create a new file named qty_change.js in app > code > Mageplaza > Mymodule > view > frontend > web > js > view > product > view and fill it with 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);
       }


   });
});

Now everything is quite clear in the above code. Check the initializer function: (). We have set up an observable quantity, which is a Magento 2 Knockout JS object. When called with the data-bind attribute from HTML, it will return its value. The two other functions are decreaseQty and increaseQty. When the HTML button is clicked, they will help change the value.

That’s the end. To override the default addtocart.phtml template, we need to inform Magento 2 about our custom template file. We will achieve this by modifying the layout using XML.

Let’s create a new file named catalog_product_view.xml in the app > code > Mageplaza > Mymodule > view > frontend > layout directory 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">Mageplaza_Mymodule::catalog/product/view/addtocart.phtml</argument>
           </action>
       </referenceBlock>
       <referenceBlock name="product.info.addtocart.additional">
           <action method="setTemplate">
               <argument name="template" xsi:type="string">Mageplaza_Mymodule::catalog/product/view/addtocart.phtml</argument>
           </action>
       </referenceBlock>
   </body>
</page>

Finally, we are ready with the Magento 2 module using Knockout JS. Please use the following Magento 2 CLI commands to enable and activate your module:


rm -rf var/di var/generation var/cache/* var/log/* var/page_cache/*
php bin/magento module:enable Mageplaza_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

We can also regularly use our module once it is prepared for use. Part of the Magento 2 interface is built flexibly with the help of Magento 2 Knockout JS. I hope this simple Magento 2 Knockout JS example has been understood and will be helpful for your future Magento 2 development.

Please feel free to contact us anytime if you have any questions or need assistance from our experts with your Magento development agency.

Table of content
    Jacker

    With over a decade of experience crafting innovative tech solutions for ecommerce businesses built on Magento, Jacker is the mastermind behind our secure and well-functioned extensions. With his expertise in building user-friendly interfaces and robust back-end systems, Mageplaza was able to deliver exceptional Magento solutions and services for over 122K+ customers around the world.



    Related Post

    Website Support
    & Maintenance Services

    Make sure your store is not only in good shape but also thriving with a professional team yet at an affordable price.

    Get Started
    mageplaza services