magento 2 knockout js

Do You Know How to Master Magento 2 Knockout JS?

by Lily Tran

Magento 2 relies on Knockout.js for dynamic, interactive user experiences, especially within the front-end UI components. If you’re looking to create responsive elements, improve checkout flows, or customize user interactions, Magento 2 Knockout JS is essential knowledge.

In this article, we will deepen your knowledge about how to use knockout JS in Magento 2, including data-binding and observables, allowing you to create seamless, real-time updates that enhance customer engagement and usability.

Let’s enjoy it!

Why Magento 2 chooses Knockout JS

First of all, we want to remind you about the definition of Magento Knockout JS.

As we mentioned in Part 1, Knockout JS (KO) is a JavaScript library that is used on the frontend in Magento 2.

Knockout JS is lighter and simpler than other frameworks, and Magento just needs some functions of KO such as data-binding and components. Because of simplicity, KO is more stable and reduces potential problems while other frameworks are in development.

Magento 2 Knockout JS Integration

In this part, we will explain how Magento 2 applies KnockoutJS bindings.

Initialization Magento 2 KnockoutJS

RequireJS declares the KO library in this file: module-ui/view/base/web/js/lib/knockout/bootstrap.js

Initialization magento 2 KnockoutJS

  • Ko: is an alias mapped to the Magento 2 Knockout JS library and declared in the file: module-theme/view/base/requirejs-config.js

KO magento 2 KnockoutJS

  • template/engine: the library to rewrite KO’s binding template.
  • bindings/bootstrap: custom bindings of this Magento file: module-ui/view/base/web/js/lib/knockout/bindings/bootstrap.js

custom bindings magento 2 KnockoutJS

Besides Magento’s custom binding, repeat and fast-foreach are the two libraries written by communities.

  • extender/observable_array: is used to write more functions for Observable arrays, such as ‘each,’ ‘map,’ ‘filter’,…

Template Engine

When you work with Ko’s templates, you need to declare an HTML snippet on Client side 1. This HTML code snippet is covered by the <script type=”text/html” id=”my_template”> tag. 

In Magento, templates are rendered from the Service side depending on how you process a page. Ko’s components can make a template be loaded from a URL on the server-side. However, that template must be assigned to a ViewModel while it is necessary to get flexibility between ViewModel and Template.

Hence, Magento rewrites KO’s templates in order to download and cache templates from the server-side.

The engine template is handled in this file: module-ui/view/base/web/js/lib/knockout/template/engine.js

To prove the template render from a URL, let’s take a look at the following example:

  • Create a controller and insert a block into the controller’s layout, with template phtml as Bss_KOExample::template_engine.phtml.
  • Create app\code\Bss\KOExample\view\frontend\templates\template_engine.phtml with this content:
<div data-bind="template: {name: 'Bss_KOExample/template_engine', data: {}}"></div>

Magento creates a URL for the above template and links to this file: view\frontend\web\template\template_engine.html

  • Create a file: app\code\Bss\KOExample\view\frontend\web\template\template_engine.html with the content:
<p data-bind="style:{color:'red'}">Magento Knockout JS Template Engine</p>.

Access the route to see that the Magento 2 Knockout template is already loaded from the server:

template magento 2 KnockoutJS

Components

As you already know, the Component Binding of KO contains ViewModel and Template.

The Magento’s Component based on the KO’s Component becomes uiComponent including Component (corresponding to ViewModel) and Template. Magento was created to aim at flexibly processing many components on a page and nested components (including child components).

There are two ways to declare a Component:

  • Declare Component from Layout (XML file): Create a node inside the Block tag, following this structure:
<arguments>
    <argument name="jsLayout" xsi:type="array">
        <item name="components" xsi:type="array">
            <item name="component-alias" xsi:type="array">
            </item>
        </item>
    </argument>
</arguments>

*Note: <item name=”component-alias” xsi:type=”array”> ⇒ Declaring the identifier of a component

⇒ Let’s take a look at an example:

<block class="Bss\CatalogSearch\Block\ListProduct" name="catalogsearch.product_list"
   template="Bss_CatalogSearch::catalog/products.phtml">
 <arguments>
   <argument name="jsLayout" xsi:type="array">
      <item name="components" xsi:type="array">
        <item name="productList" xsi:type="array">
         <item name="component" xsi:type="string">Bss_CatalogSearch/js/catalog/products</item>
         <item name="config" xsi:type="array">
            <item name="template" xsi:type="string">Bss_CatalogSearch/catalog/product-list</item>
         </item>
       </item>
      </item>
    </argument>
  </arguments>
</block>
  • Directly declare by Script tag (PHTML file)

For instance:

<script type="text/x-magento-init">
     {
        "*": { "Magento_Ui/js/core/app": {
            "components": {
                "productList": { // => component identifier
                        "component": "Bss_CatalogSearch/js/catalog/products",
                        "config": {
                            "template": "Bss_CatalogSearch/catalog/product-list"
                        }
                    }
                }
            }
        }
     }
</script>

(The keyword “component” is considered as ViewModel in Ko Component).

The “product list” Node is declared in the “components” node with Components as “Bss_CatalogSearch/js/catalog/products”, and Template file as “Bss_CatalogSearch/catalog/product-list.”

In case you declare Component from the Layout, you need to initialize Component that Component as below:

<script type="text/x-magento-init"> 
    { 
        "#products-wrapper": {
            "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout(); ?> 
        } 
    } 
</script>

getJsLayout() parses content from the jsLayout node from the layout and converts it into JSON.

Magento_Ui/js/core/app

It parses components that are declared in JsLayout. Then call the layout.js file (vendor/magento/module-ui/view/base/web/js/core/renderer/layout.js) to initialize UiComponent instance for each Component and save components in uiRegistry. 

uiRegistry

Source code: vendor/magento/module-ui/view/base/web/js/lib/registry/registry.jsuiRegistry caches components that are declared into memory. You can read more information about uiRegistry HERE.

uiComponent

uiComponent (or uiCollection) is the base component of Magento 2 Knockout JS. It means that every declared component needs to be extended. UiComponent initializes and renders child components that are declared inside. You can file its source code in the file: vendor/magento/module-ui/view/base/web/js/lib/core/collection.js.

Scope binding

Magento creates ‘scope’ binding to apply Component in the View layer (phtml file). It helps determine the identifier of the corresponding Component in JSON data to load the Javascript processing section, thereby rendering the Magento 2 Knockout template.

For example, you declare “product list” component in the layout:

<item name="components" xsi:type="array">
    <item name="productList" xsi:type="array">
        <item name="component" xsi:type="string">Bss_CatalogSearch/js/catalog/products</item>
        <item name="config" xsi:type="array">
            <item name="template" xsi:type="string">Bss_CatalogSearch/catalog/product-list</item>
        </item>
    </item>
</item>

To map this content to View, you use:

<div id="products-wrapper" data-bind="scope: 'productList'" class="container-default">
...
</div>

*Note: scope binding is only applied to the main component, not to the child component.

getTemplate function

To call Magento 2 Knockout Template from a Component, you use the syntax:

<!-- ko template: getTemplate() --><!-- /ko →

This function is declared in the file: module-ui/view/base/web/js/lib/core/element/element.js

/**
* Returns path to elements' template.
*
* @returns {String}
*/
getTemplate: function () {
   return this.template;
},

*Note: To declare Template from Component (JS file), you add the following config into ViewModel:

defaults: {
   template: 'template_path'
}

For example, the file: module-customer/view/frontend/web/js/view/authentication-popup.js.

gettemplate function magento 2 KnockoutJS

How to Use Knockout JS in Magento 2 Checkout Page

As you know, Magento 2 uses the Knockout JS framework to build the dynamic checkout page.

So in this part, we will dive into all aspects of the Magento 2 checkout page.

How Magento 2 Knockout JS works on the checkout page

checkout page magento 2 Knockout JS

Components on the checkout page

A checkout page includes elements such as authentication, steps, message, shipping address, billing address, shipping methods, estimation, payment methods, sidebar, and so on. Each element is declared as an uiComponent with a Magento 2 Knockout template and configs of that element. You can also declare child elements, and arrange and customize the display right on the XML file.

checkout magento 2 Knockout JS

Magento processes the checkout_index_index.xml layout via the LayoutProcessor class, parses nodes to create an array, and then converts to JSON format. That JSON data will be rendered out of the Block view (phtml file) through the getJsLayout () function declared in the script tag.

JSON data can be:

JSON data

window.checkoutConfig = <?= /* @escapeNotVerified */ $block->getSerializedCheckoutConfig() ?>;

Configs of the checkout page are declared in JavaScript by the Global variable. This becomes a condition to process uiComponents.

Magento_Ui/js/core/app initializes components and child components declared in JSON data into uiComponents, and caches to memory and arrange positions.

The main component of the page with the identifier as checkout declares with the base component as uiComponents because logics are processed in the child component of the “child” node.

The main template of the checkout page as Magento_Checkout/onepage will be rendered out of view-layout by Magento’s custom binding “scope”:

< div id="checkout" data-bind="scope:'checkout'" class="checkout-container">

Child components are respectively declared in the “children” node. Child Component also includes nodes declaring Component and Config/Template which is similar to Component. Also, the “displayArea” node is used to determine the position of Child Component on DOM. To render Child Component, you should use the “getRegion” binding. For example:

<!-- ko foreach: getRegion('estimation') -->
   <!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko→

one-page checkout magento 2 KnockoutJS

In addition, you can use “sortOrder” node to arrange the display order of the child Component conveniently. The bigger the value is, the lower position the component can stay at. Magento_Ui/js/core/app initially parses JSON data according to the value order of “sortOrder”. 

For example:

<item name="sortOrder" xsi:type="string">0</item>

Notices to work on the checkout page

To change elements on the checkout page, you have to interfere with the “jsLayout” node because LayoutProcessor uses this node to parse the declaration of elements. There are two ways to change “jsLayout”:

  • Interfere with the checkout_index_index.xml layout to customize the layout

customize layout

Declare a plugin for the class: \Magento\Checkout\Block\Checkout\LayoutProcessor::process to process the array. This method should be used when you have to implement complex logic or can not perform on XML files, such as handling display conditions, custom templates, etc.

  • Use RequireJS mixins, and maps to customize uiComponent and template
  • Components must extend “uiComponent” according to the following principle:
define([
   'uiComponent',
], function (Component) {
   return Component.extend({
       initialize: function () {
           this._super();
       }
   })
})
  • To disable a Component, use the “componentDisabled” node in the “config” node, for example:
<item name="config" xsi:type="array">
    <item name="componentDisabled" xsi:type="boolean">true</item>
</item>
  • To hide a component, use the “visible” node, for example:
<item name="visible" xsi:type="boolean">false</item>

For additional information on Customize Checkout topics, please take a look at the following articles: Max Pronko Checkout Customizations in Magento 2

Wrap Up

So this is the end of part 2, which is the last part of the series of mastering Magento 2 Knockout JS.

I hope that the series of Knockout JS and Magento 2 Knockout JS will give you helpful knowledge to better your understanding and contribute to your work.

BSS Commerce is one of the leading Magento extension providers and web development services in the world. With experienced and certified Magento developers, we commit to bringing high-quality products and services to optimize our business effectively.

CONTACT NOW to let us know your problems. We are willing to support you every time.

Next Reading Suggestions

© 2019 BSS Commerce owned by THANH CONG INTER ., JSC. All Rights Reserved.
Business registration certificate no. 0106064469 issued by Hanoi Department of Planning and Investment on 19 December 2019.
Legal Representative: Mr. Nguyen Quang Trung.