# Adding a `ComponentViewModel`

**By default, any Loki Component ships with an instance of `Loki\Components\Component\ComponentViewModel` which is instantiated in the PHTML template as a variable `$viewModel`. However, it could be that you want to customize the methods in that ViewModel. For instance, some ViewModel properties can be set via the XML layout, but what if they need to be dynamic?**

## Adding a ViewModel to the component definition
First, add the following to the XML file `etc/loki_components.xml`:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Loki_Components:etc/loki_components.xsd">
    <component 
        name="loki-components.example"
        viewModel="YireoTraining\ExampleLokiComponent\Component\Example\ExampleViewModel"
        repository="YireoTraining\ExampleLokiComponent\Component\Example\ExampleRepository"
    />
</components>
```

Clean the configuration cache afterwards.

## Creating a `ComponentViewModel` class
A new PHP class `YireoTraining\ExampleLokiComponent\Component\Example\ExampleViewModel` now needs to be created in our module. This class is dubbed the `ComponentViewModel` class in a component, because it extends upon a class `Loki\Components\Component\ComponentViewModel`.

```php
namespace YireoTraining\ExampleLokiComponent\Component\Example;

use Loki\Components\Component\ComponentViewModel;

class ExampleViewModel extends ComponentViewModel
{
}
```

There are no specific methods you need to declare to make this work.

## Overriding the AlpineJS component name
Every time a Loki Component is being rendered, an `x-data` argument is automatically added to its PHTML template output, referring to the AlpineJS component that should be used. By default, this is the AlpineJS component `LokiComponent`. However, we can override this if we want to override the default behaviour.

In the previous document, we built an example where custom values `value1` and `value2` would need to be defined. First of all, we'll change the default AlpineJS component into a new component `ExampleLokiComponent` (which has not been defined yet).

```php
namespace YireoTraining\ExampleLokiComponent\Component\Example;

use Loki\Components\Component\ComponentViewModel;

class ExampleViewModel extends ComponentViewModel
{
    public function getJsComponentName(): ?string
    {
        return 'ExampleLokiComponent';
    }

    public function getJsData(): array
    {
        return [
            ...parent::getJsData(),
            'value1' => 'Hello World 1',
            'value2' => 'Hello World 2',
        ];
    }
}
```

## Adding custom properties to the AlpineJS component
When you are adding new properties via the `getJsData()` method, these properties are automatically added to the Alpine.js component. Note that these properties are read-only and skipped by most Alpine.js DevTools unless (!) they are also explicitly defined in the Alpine.js component (see below).

Instead of overriding the method `getJsData()`, you can add the PHP Attribute `\Loki\Components\Attribute\JsProperty` to a specific method, declare the property name, while the return of the method becomes the value of the property:

```php
namespace YireoTraining\ExampleLokiComponent\Component\Example;

use Loki\Components\Component\ComponentViewModel;
use Loki\Components\Attribute\JsProperty;

class ExampleViewModel extends ComponentViewModel
{   
    #[JsProperty(name: 'value1')]
    public function getValue1(): mixed
    {
    	return 'Hello World 1';
    }
    
    #[JsProperty(name: 'value2')]
    public function getValue2(): mixed
    {
    	return 'Hello World 2';
    }
}
```

When overriding the `getJsData()` method, always make sure to call the parent method first:

```php
    public function getJsData(): array
    {
        return [
            ...parent::getJsData(),
            'value1' => 'Hello World 1',
            'value2' => 'Hello World 2',
        ];
    }
```

## Adding a new AlpineJS component
To add a new AlpineJS component `ExampleLokiComponent`, let's add a block to the XML layout first:

```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
    <update handle="loki_components"/>
    
    <body>
        <referenceBlock name="loki.script.component">
            <block
                name="loki-components.script.component.example"
                template="YireoTraining_ExampleLokiComponent::script/component/example-component.phtml"/>
        </referenceBlock>
    </body>
</page>
```

The PHTML template could look as follows:
```php
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('ExampleLokiComponent', () => ({
            ...LokiComponentType,
            value1: '',
            value2: '',
            submit() {
                this.post();
            }
        }));
    });
</script>
```

And now we can go back to the PHTML template and call upon this new method `submit`:

```html
<div>
    <button @change="submit">Refresh</button>
</div>
```

As you can see, our Alpine.js component inherits from `LokiComponentType`. However, because `LokiComponentType` is just a JavaScript object, we can also use other objects or multiple objects to borrow properties from. This flexibility is why Loki focuses heavily on creating reusable component types (objects extending from `LokiComponentType`) or component partials (objects to be mixed into component types).
