How Magento's JavaScript Block Loader Works
Published: December 11, 2022
I’ve been looking at a Magento site where multiple loaders show up in different areas on the cart page. The loaders look something like this:
I did a bit of a deep dive into what actually causes these loader to show up. In this post I’ll share my findings.
Magento_Ui/js/block-loader
Loaders that show up in a specific area of the page are typically powered by Magento_Ui/js/block-loader
.
This component creates a custom knockout binding called blockLoader
ko.bindingHandlers.blockLoader = {
/**
* Process loader for block
* @param {String} element
* @param {Boolean} displayBlockLoader
*/
update: function (element, displayBlockLoader) {
element = $(element);
if (ko.unwrap(displayBlockLoader())) {
blockLoaderElement.done(addBlockLoader(element));
} else {
blockLoaderElement.done(removeBlockLoader(element));
}
}
};
If the binding value is true
the addBlockLoader
function will be called (and if false
it will call removeBlockLoader
.
Using the binding
Magento_Checkout/js/view/cart/totals
provides a good example of how to use the binding.
The component has an isLoading
property, which is mapped to isLoading
in Magento_Checkout/js/model/totals
define([
'jquery',
'uiComponent',
'Magento_Checkout/js/model/totals',
'Magento_Checkout/js/model/shipping-service'
], function ($, Component, totalsService, shippingService) {
'use strict';
return Component.extend({
isLoading: totalsService.isLoading,
Then in the template file Magento_Checkout/template/cart/totals.html
we can see the custom binding set on the wrapper div.
<div class="table-wrapper" data-bind="blockLoader: isLoading">
In Magento_Checkout/js/model/totals
we can see that isLoading
is an observable, initially set to false
.
isLoading: ko.observable(false),
If we search for isLoading
across the codebase we find many places that toggle the value, which will cause the loader to show. For example in Magento_Checkout/js/action/get-totals.js
totals.isLoading(true);
return storage.get(
resourceUrlManager.getUrlForCartTotals(quote),
false
).done(function (response) {
var proceed = true;
totals.isLoading(false);
Monitoring loader display
One thing that could be interesting to put in place is monitoring to track how much time the site users spend seeing loaders. Doing so would require patching Magento_Ui/js/block-loader
.
Adding a tracker that flags when the block loader shows and when it’s hidden can be done as follows.
addBlockLoader
function addBlockLoader(element) {
console.log(`addBlockLoader ${element.context.className} ${element.context.id} ${performance.now()}`);
removeBlockLoader
function removeBlockLoader(element) {
if (!element.has(blockLoaderClass).length) {
return;
}
console.log(`removeBlockLoader ${element.context.className} ${element.context.id} ${performance.now()}`);
From here we can extrapolate how much time the loader was visibile for, and on which area of the page.
Obviously in the real world we would not use console.log
and instead something like newrelic.addPageAction
(assuming the project is using New Relic).
Wrap Up
That’s all for now. Hopefully you found this helpful because I sure as heck was scratching my head trying to figure out what was causing the spinners to show up.