Hey to Amasty blog readers! Today we are going to talk about what is JS Knockout, examples of using it in e-commerce, why Magento 2 requires JS config, and 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.
Hey! Do you want to increase the checkout conversion rate in your Magento 2 store? Try the One Step Checkout module. Enable Google Auto Suggest, add delivery date option, customize the design of the checkout page, and boost your sales!
Knockout implements Model-View-View Model (MVVM) design pattern with templates and allows creating dynamic pages, which change with respect to the actions of a user. The 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 date picker, popups, and other custom design elements. Despite the benefits, implementing Magento 2 solutions with Knockout JS and Magento 2 JS translation is quite complicated for many developers.
So, in this tutorial, we look at how JS Knockout works and how to use it in your Magento 2 projects. But KO is only one of the JS libraries that Magento 2 uses, there are many others, for example, jQuery, Underscore, etc.
Article summary
- How Knockout JS works in Magento 2
- How Knockout JS works in Magento 2 UI components
- The great thing about Knockout is
- Knockout observables in action
- Knockout for data exchange between UI components
- Knockout and using a UI component in a page layout
- Knockout and server to UI component data transfer
- Bottom line
How Knockout JS works in Magento 2
Added with several changes to Magento 2, KO is now widely used for variable bindings, using the data-bind attribute, adding JavaScript event handlers with the help of custom bindings, and tracking them. Magento 2 documentation shows in detail how the library works for these purposes. Overall, the use of Knockout JS in Magento 2 regards building rich components of a page frontend that, with KO in place, contribute a lot to UI convenience.
How Knockout JS works with 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 or, in some cases, extends it. A Model here is your application’s stored data, the View is a relevant HTML document that describes how the component is rendered, and a View Model is a pure-code representation of the data and operations on a UI.
The frontend js files are conventionally located as follows:
app/code/<your_module_namespace>/<your_module_name>/view/<area>/web/js/
And the area can be frontend, adminhtml, or base.
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/model/quote.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 View Model contains information about all the functions that will be used in the View part of the component. Also, the View Model is used to declare observable variables.
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 ViewModel with the template.
For more information about Knockout directives and binding, check the Knockout official documentation.
The great thing about Knockout
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 an instant reaction to changes of certain objects.
Knockout observables in action
Now, let’s consider an example of using JS knockout observables in Magento 2. We access the View 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 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 Component 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 Extra Fee Module (Amasty_Extrafee/js/view/summary/block).
In the Component Amasty_Extrafee/js/view/summary/block, we see:
And the View located in Amasty/Extrafee/view/frontend/web/template/fee/block.html looks like this:
Knockout and server to UI component data transfer
Some Magento 2 pages include a LayoutProcessor responsible for component generation. To convey dynamic variables to the frontend, you should create the LayoutProcessor class in a separate Module folder, for example, named Block.
The LayoutProcessor class inplements Magento/Checkout/Block/Checkout/LayoutProcessorInterface.
Then, in the LayoutProcessor class, we implement 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 JS 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.
P.S. Special thanks to Oleg Ivanov for the help with this post.