Hey to Amasty blog readers!

Today we are going to talk about using Knockout in Magento 2 frontend solutions.

Knockout (or KO) is a JavaScript library that is widely used to create the frontend part of Magento 2, the checkout page in particular. Knockout implements Model-View-View Model (MVVM) design pattern with templates and allows creating dynamic pages, which change with respect to actions of a user.

Introduction of this library brought significant positive changes to Magento 2 development and made it possible to create a much wider range of responsive frontend components, such as datepicker, popups, and other custom design elements.

Despite the benefits, implementing Magento 2 solutions with Knockout is quite complicated for many developers. So, today we look at how KO works and how you can use it in your Magento 2 projects.

How Knockout works in Magento 2

Added with several changes to Magento 2, KO is now widely used for variable bindings, creating JavaScript events and tracking them. Magento 2 documentation shows in detail how the library works for these purposes. Overall, the using of Knockout in Magento 2 regards building rich components of a page frontend that, with KO in place, contribute a lot to UI convenience.

How Knockout works in Magento 2 UI components

Magento 2 UI components are used to show particular UI elements, such as tables, buttons, tabs, dialogs, and more. UI components consist of an XML declaration that specifies its configuration settings and inner structure, a JavaScript class, and Related template(s) that use the Knockout bindings.

To be more specific, a UI component implements a part of the MVVM design pattern. A Model here is a piece of Knockout code that contains the business logic of the component, and a View is a relevant HTML document that describes how the component is rendered.

The frontend Knockout files are conventionally located as follows:

app/code/<your_module_namespace>/<your_module_name>/view/frontend/web/js/

And the frontend files related to JavaScript file templates are usually located as below:

app/code/<your_module_namespace>/<your_module_name>/view/frontend/web/template/

To make it more vivid, consider the checkout component as an example.

First, we access the Model via module-checkout\view\frontend\web\js\view\review\actions\default.js, and see the following code representation:

The path to the component template is shown in the defaults section of the component and takes the form of an object with the key named template. The Model contains information about all the functions that will be used in the View part of the component. Also, the Model is used to declare all observable variables, for example, placeOrder.

Second, the View is located in the module-checkout\view\frontend\web\template\review\actions\default.html and looks as below:  

The component template contains special Knockout directives used for binding the View with the Model.

For more information about Knockout directives and binding, check the Knockout official documentation.

The great thing about Knockout is

One using Knockout will definitely appreciate observables. Observables are special JavaScript objects that can notify subscribers about changes and automatically detect dependencies. Observables got their name for the reason that they can be observed, which means that other code can subscribe and react to their changes. This way observables allow creating a dynamic connection between variables and data so as to refresh template data when a component data changes.

While observables are perfect for detecting and responding to changes of one object, working with changes in a collection of things requires an observableArray. The observableArray tracks which objects are in the array, yet not their state. Put simply, the observableArray notifies subscribers when objects are added, removed, filtered, rearranged, etc.

Overall, observables help developers create logic for diverse cases that require instant reaction to changes of certain objects.

Knockout observables in action

Now, let’s consider an example of using observables. We access the Model via the path module-customer\view\frontend\web\js\view\authentication-popup.js and declare the isLoading variable as observable using a relevant keyword – observable.

Now, let’s move on to the View part which also contains the isLoading variable. The path is: module-customer\view\frontend\web\template\authentication-popup.html.

Finally, take a look at an example of an event subscription:

We access module-checkout/view/frontend/web/js/view/cart/totals.js and see:

In the code, you can see that subscription regards the Totals observable. Now when the Totals changes, a related function will trigger the Resize event.

Knockout for data exchange between UI components

In Magento 2, all variables defined in a UI component are limited by the visibility area of this component. However, components can still exchange data and variables. For this, you should create a special data model in the form of a standard JavaScript object and, by using the define directive, embed this data model into the component with which you want to exchange data. The functions and variables of this data model can be used in the target component as well.

 JavaScript objects are transferred by reference. So if an observable variable that belongs to a data model changes, this change will affect the source object in which this variable is defined. Put simply, when components share variables via a data model, any update of an observable variable will affect every component that uses this variable and the shared data model.

Here’s an example of the data model of the Checkout Module that we access via module-checkout/view/frontend/web/js/model/shipping-service.js:

Pay attention to the setShippingRates method that affects an observable variable named shippingRates. This method is used in the following model as well module-checkout/view/frontend/web/js/model/shipping-rate-processor/customer-address.js:

In this model, we apply dependencies to transfer the shippingService model into the customerAddress model and use the method setShippingService of the shippingService model. Also, the observable isLoading variable is updated. This way, we connect the customerAddress model to the shippingService model and enable data exchange between the two.

Knockout and using a UI component in a page layout

Now, let’s look at an example of implementing a UI component in a layout file. After we access the XML file located in Amasty/Extrafee/view/frontend/layout/checkout_cart_index.xml, we get:

In the component section, we set the route to the Model file responsible for the Knockout code of the component. The View template can be specified either in the Knockout code or in the config section of the layout of the page (that is checkout_cart_index in our case).

This is how it looks like in the example above:

Below you can see fragments of the code of the component Block in the Amasty Extrafee Module (Amasty_Extrafee/js/view/checkout/cart/block).

In the Model Amasty/Extrafee/view/frontend/web/js/view/checkout/cart/block.js, we see:

And the View model located in Amasty/Extrafee/view/frontend/web/template/checkout/cart/block.html looks like this:

Knockout and server to UI component data transfer

Every Magento 2 page includes a LayoutProcessor responsible for a component generation. To convey dynamic variables to the frontend, you should create the LayoutProcessor class in the Module folder named Block.

The LayoutProcessor class is inherited from \Magento\Checkout\Block\Checkout\LayoutProcessorInterface.

Then, in the LayoutProcessor class, we realize the Process method which accepts the input of a jsLayout  variable. The jsLayout  is rather an array variable than just a variable as it contains information about all components that belong to this page.

Using this array variable, we can refer to our component and change/add any variables that we need to use on the frontend of the component.

Check this example of realizing the Process method in Amasty/Extrafee/Block/Cart/LayoutProcessor.php:

Bottom line

Today, we looked at how Knockout works in Magento 2. In particular, we learned to use Knockout observables in UI components, add UI components to a page layout, enable data exchange between UI components, and allow data transfer from a server to the frontend of a component.

Hope this article helped you to understand Knockout better and looking forward to your comments and ideas in the feed below.