Extending existing Alpine.js components can be done in various ways. We'll go through this in this document.
The regular way of extending Alpine.js components is to create your own new component and extend from the available component types. For instance, you could create a new block via the XML layout as follows:
<referenceBlock name="loki.script.component">
<block
name="yireo-training-example.script.components.example"
template="YireoTraining_Example::script/component/example.phtml"/>
</referenceBlock>
Within the PHTML template, you could then extend upon
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('ExampleComponent', () => ({
...LokiComponentType,
initHelloWorld() {
console.log('Hello Example Component);
}
}));
});
</script>
Finally, you could define some other block with template as a Loki Component with the component name being ExampleComponent
, as discussed elswhere.
The upside of this approach is that you are creating a child component in a clean way, without conflicting with other components. The downside is that this approach requires a new component. What about modifying existing components?
The LokiComponentExtender
object allows for a neat way to extend upon existing Alpine.js components. Actually, it replaces the original Alpine.data()
factory and replaces it with support for LokiComponentExtender.extensions
callbacks.
To use this, first, create a new script for your specific mixin:
<referenceBlock name="loki-components.script.component-extender">
<block name="example" template="YireoTraining_Example::script/mixin/example.phtml" />
</referenceBlock>
Next, within the PHTML template, define a new mixin via the LokiComponentExtender.add()
method: The first argument is an identifier for your mixin. The second argument is a callback that should be called whenever Alpine.data()
is run. In this case, we return an object with a method initHelloWorld()
. And thanks to the fact that - with Loki Components - any method starting with init
is automatically run as part of the Alpine.js init()
procedure, the console log is printed. Note that this runs on any Loki Components.
<script>
LokiComponentExtender.add('ExampleMixin', () => {
return {
initHelloWorld() {
console.log('Hello', this.id);
}
}
});
</script>
What about adding this to a specific component only? That's easy: The callback (the second argument to LokiComponentExtender.add()
) receives a componentName
. In the example below, the mixin is only applied if the component name is LokiFieldComponent
(so: x-data="LokiFieldComponent"
).
<script>
LokiComponentExtender.add('ExampleMixin', (componentName) => {
if (componentName !== 'LokiFieldComponent') {
return false;
}
return {
initHelloWorld() {
console.log('Hello', this.id);
}
}
});
</script>
A shorter syntax for the same thing runs via the addMixin()
method:
<script>
LokiComponentExtender.addMixin('ExampleMixin', 'LokiFieldComponent', {
initHelloWorld() {
console.log('Hello', this.id);
}
});
</script>
Within Loki Components, a component like LokiFieldComponent
could be used multiple times. (Because of this, we go through the trouble of talking about component partials, component types, components and component instances.) Every instance, however, has its own uniq title (x-data="Foobar"
) and ID (equal to the block name and element ID). Unfortunately, these are determined at run time, so you can't check for them with the LokiComponentExtender
.