When a LokiComponent is busy with the AJAX call (storing its value in Magento and/or updating HTML elements on the page), the Alpine component allows you to hook into this loading state in various ways.
loading
Each LokiComponent ships with an Alpine property loading
which is false
by default, but which is set to true
while the POST request is running. Elsewhere, the property loading
is watched for further functionality. However, you could already show a simple loading text, based on this:
<span x-show="loading">Loading ...</span>
A fancier example could be the following:
<div x-cloak x-show="loading" class="<?= $css('inline-block mr-3 w-4 animate-spin fill-white', 'icon') ?>">
<?= $imageOutput->get('Yireo_LokiFieldComponents::images/spinner.svg') ?>
</div>
aria-busy
attributeA single LokiComponent update could also involve a refresh of other HTML attributes on the page. While making the AJAX call, each target is resolved into the corresponding HTML element and the aria-busy
HTML attribute is set. This also counts for the component itself.
If the HTML element contains a child element that shows some kind of loading image, then this child element could be hidden by default but shown again via CSS based upon this aria-busy
attribute. A lot of LokiCheckout components use this logic as follows:
<div class="<?= $css('relative') ?>">
<?= $blockRenderer->html($block,'loki-field-components.utils.loader-overlay') ?>
...
</div>
Here the <div>
element equals the root of the Alpine component, where aria-busy
is set. Next, the block loki-field-components.utils.loader-overlay
is used to display an image loader. By default, this block has the display:none
CSS rule set (via the Tailwind class hidden
).
With the corresponding CSS the loader overlay is shown conditionally:
[aria-busy] > .loader-overlay {
display: flex;
}
loading
property in other Alpine componentsThe post()
method tries to map each target name into another LokiComponent on the page. And if that LokiComponent is found, the loading
property of that LokiComponent is toggled as well.
This causes the possibility that a single component update triggers multiple loaders on the page.
showLoader
Above it was already explained how a loader overlay was shown. With many field components, a smaller loader image is shown within the field input itself:
<div x-cloak x-show="showLoader" class="<?= $css('field-loader flex absolute inset-y-0 right-0 pt-1 pr-2', 'loader') ?>">
<?= $blockRenderer->html($block, 'loki-field-components.utils.loader-small') ?>
</div>
Apart from different Tailwind classes, it is the same logic. Except for that a different Alpine property showLoader
is used. While the loading
property is set once the AJAX call is initiated, the showLoader
property toggles only after a slight delay. This delay is set via the property showLoaderTimeout
. (Note that you can change any property of a LokiComponent via the getJsData()
method of its ViewModel and with that, the XML layout.)
The reason for this is an esthetic reason: In a production environment, often component updates are fast and take little more than 300ms. If every single change would popup that loader for just a split second, it will confuse users. Because of this, the delayed showLoader
approach allows for showing the loader only, if the user really needs to be informed of the fact that the update is not instant.
The default showLoaderTimeout
is 700ms, which should make for a visually appealing updating experience.
loading
for fieldsA complementary way of styling the loading state of fields is by using the CSS class loading
. This is added to all components that are derived from the LokiFieldComponent
component data (or actually derived from the LokiFieldComponentType
component type).
A simplified version of such a component might look like the following:
<div x-data="LokiFieldComponent" x-title="FooBar">
<script ref="initialData" type="text/x-loki-init">{}</script>
<input x-model="value" @change="submit" x-ref="field" class="foobar" />
</div>
Here, the <div>
element serves as the root of an Alpine component FooBar
which uses the LokiFieldComponent
component data. The <input>
element is connected to the Alpine property value
via x-model
. And every time the value is changed, it is posted to the server by using submit()
.
Now note the x-ref
. Within the component logic, this is used by a watcher on the Alpine property loading
. Whenever the loading
value is true, the CSS class loading
is added. Whenever the loading
value is false, the CSS class loading
is removed again.
Additionally, the aria-busy
attribute and disabled
attribute are also added to the field.
The resulting HTML while loading might look like the following:
<div x-data="LokiFieldComponent" x-title="FooBar" aria-busy>
<script ref="initialData" type="text/x-loki-init">{}</script>
<input x-model="value" @change="submit" x-ref="field" class="foobar loading" aria-busy disabled />
</div>