KNOCKOUT JS IN MAGENTO 2

Knockout was one of the most significant changes in Magento 2 after transitioning from Magneto 1. Knockout is a Javascript library that follows the MVVM (Model-View-View-Model) Pattern, and as we know, Magento 2 also follows the MVVM pattern. I think this is one of the reasons why knockout is introduced in Magento 2. Knockout is used for creating the frontend in Magento 2. It provides a great way to create interactive frontend data-bound components within your Magento 2 store.

Knockout Js is used almost on every page but extensively used on the checkout page as the checkout of Magento 2 is built up from a series of Knockout JS components which are rendered using the Knockout JS templating system.

The implementation of Knockout JS in Magento 2 is slightly complex. So, we will explain the basic concept of Magento 2 Knockout JS in this article. But before we start, we would like to know if you spent some time on tutorial of Knockout JS.

In this article, we will create a module in which we will add quantity increment/decrement buttons on the product page and set the Add to Cart button, enabled only when qty is greater than 4, so that a minimum 5 products would be added to the cart from the product page.

So, let’s start with creating a module

Step 1: Create registration.php in app/code/<Vendor>/<Module>Here we are using the Vendor name as Bizspice and the Module name as Qtychange.

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

Step 2: Create module.xml in app/code/Bizspice/Qtychange/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="Bizspice_Qtychange" setup_version="1.0.0"></module>
</config>

Step 3: As now we have set up the basic files for the Module we will change the behavior of Quantity on the product page. By default, the quantity field on the product page is rendered from

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

So, copy this file in your module so the location of this file will be-

app/code/Bizspice/Qtychange/view/frontend/template/catalog/product/view/addtocart.phtml

And add this code anywhere in your file.

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

Here we have created our component named qty_change and indicated that we added a js file named qty_change which will bind our frontend HTML. So, now to add the increase and decrease button, we will add this code in our phtml file

<div class="control new-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>

Here we have used the data-bind attribute in div as well as the input and button field. The data-bind connects our HTML file with the Javascript function that we have defined in the component inside script tag given in the above code. Every data-bind function invoked will be searched in the qty_change component. The data-bind events are defined in qty_change.js.

We have added data-bind=”scope: ‘qty_change’ in div, which means the scope of this component (i.e., qty_change) will be till that particular div.

Now we will add “Add To cart Button” in our file, which should enable when the quantity is more than 4. To do this, replace this code.

<div class="actions" data-bind="scope: 'qty_change'">
         <button data-bind="enable: qty() > 4"
               type="submit"
               title="<?= /* @escapeNotVerified */ $buttonTitle ?>"
               class="action primary tocart"
               id="product-addtocart-button">
               <span><?= /* @escapeNotVerified */ $buttonTitle ?></span>
         </button>
         <?= $block->getChildHtml('', true) ?>
</div>

Here we have added data-bind=”enable: qty() > 4″ in button (where qty function is defined in qty_change.js). That means enabling the button when the quantity is greater than 4.

So now, finally, addtocart.phtml will look like this.

<?php
/**
 * Copyright © Magento, Inc. 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><?= /* @escapeNotVerified */ __('Qty') ?></span></label>
                              <div class="control">               
                                      <div class="control new-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>
                                     <script type="text/x-magento-init">
                                       {
                                          "*": {
                                                  "Magento_Ui/js/core/app": {
                                                         "components": {
                                                             "qty_change": {
                                                                  "component": "Bizspice_Qtychange/js/view/product/view/qty_change",
                                                                  "defaultQty": <?php echo $block->getProductDefaultQty() * 1 ?>
                                                              }
                                                         }
                                                   }
                                             }
                                        }
                                     </script>
                             </div>
             </div>
             <?php endif; ?>
             <div class="actions" data-bind="scope: 'qty_change'">
                  <button data-bind="enable: qty() > 4"
                        type="submit"
                        title="<?= /* @escapeNotVerified */ $buttonTitle ?>"
                        class="action primary tocart"
                        id="product-addtocart-button">
                             <span><?= /* @escapeNotVerified */ $buttonTitle ?></span>
                  </button>
                  <?= $block->getChildHtml('', true) ?>
             </div>
         </div>
</div>
<?php endif; ?>
<script type="text/x-magento-init">
        {
              "#product_addtocart_form": {
                    "Magento_Catalog/js/validate-product": {}
              }
         }
</script>

Step 3: Now, we will add the qty_change component, which we have defined in our addtocart.phtml

So, create qty_change.js in app/code/Bizspice/Qtychange/view/frontend/web/js/view/product/view/

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

Here we have defined those functions which we bind in our HTML file. We initialized qty observable that will return the value when data-bind attribute is invoked from HTML. Similarly, with decreaseQty and increaseQty function, those are invoked from the button in HTML.

Step 4: Now, we will tell Magento to use our version of addtocart.phtml instead of default addtocart.phtml. For this, we will create a layout file named catalog_product_view.xml and define our addtocart.phtml.

For this, create catalog_product_view.xml in

app/code/Bizspice/Qtychange/view/frontend/layout

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

And here, we modify the template path using the layout file. Now, addtocart.phtml of our module is used instead of the default.

Here our Quantity increment/decrement buttons are done.

And Add to cart is disabled if the quantity is not greater than 4 (in the above image, Quantity is greater 4, and hence Add to Cart is enabled).