# Extending Alpine.js components

**Extending existing Alpine.js components can be done in various ways. We'll go through this in this document.**

## Creating a new Alpine.js component
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:

```xml
<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

```html
<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?

## Mixing your own data into 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:

```xml
<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.

```html
<script>
    LokiComponentExtender.add('ExampleMixin', () => {
        return {
            initHelloWorld() {
                console.log('Hello', this.id);
            }
        }
    });
</script>
```

## Mixin for one specific component name
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"`).

```html
<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:
```html
<script>
    LokiComponentExtender.addMixin('ExampleMixin', 'LokiFieldComponent', {
        initHelloWorld() {
            console.log('Hello', this.id);
        }
    });
</script>
```

## Mixin for one specific component title or ID
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`.
