Earlier today we released BladeX, a package that allows you to use Blade components using a Vue inspired syntax. Read all about it in my previous blogpost.

In the latest North Meets South podcast Jacob and Michael discussed BladeX and wondered if the package could be married with view models or Laravel's View Composers. Our team discussed this too last Friday but we decided to release the core features of BladeX first. After the release today it became apparent pretty fast that we needed some sort of view model support in our projects too. So we got to work!

I'm happy to share that v1.1 of BladeX now has support for view models. In this blogpost I'd like to walk you through the feature.

Using view models #

View models are used to transform data before a BladeX component is rendered. Let's make the usage clear with an example. We are going to render a select element to render a select element with some countries.

To make a BladeX component use a view model you need to tack on a call to viewModel when you register the component. The class name of the view model is pass to that method.

BladeX::component('select-field')->viewModel(SelectViewModel::class);

Before reviewing the contents of the component and the view model itself, let's take a look first on how we are going to use component.

@php
// in a real app this data would most likely come from a controller
$countries = [
    'be' => 'Belgium',
    'fr' => 'France',
    'nl' => 'The Netherlands',
];
@endphp

<select-field name="countries" :options="$countries" selected="fr" />

Next, let's take a look at what the SelectViewModel::class looks like:

class SelectViewModel extends ViewModel
{
    /** @var string */
    public $name;

    /** @var array */
    public $options;

    /** @var string */
    public $selected;

    public function __construct(string $name, array $options, string $selected = null)
    {
        $this->name = $name;

        $this->options = $options;

        $this->selected = $selected;
    }

    public function isSelected(string $optionName): bool
    {
        return $optionName === $this->selected;
    }
}

Notice that this class extends \Spatie\BladeX\ViewModel. Every attribute on select-field is being passed to the constructor. This passing is being done name based, the name attribute will be passed to a constructor argument named $name, the options attribute will be passed to $options and so on. Any other argument will be resolved out of the ioc container. This can be handy for dependency injection.

All public properties and methods of the view model will be passed to the Blade view that will render the select-field component. Public methods will be available in as a closure stored in the variable that is named after the public method in view model. This is what that view looks like.

<select name="{{ $name }}">
    @foreach($options as $value => $label)
        <option {!! $isSelected($value) ? 'selected="selected"' : '' !!} name="{{ $value }}">{{ $label }}</option>
    @endforeach
</select>

When rendering the BladeX component, this is the output:

<div>
  <select name="countries">
    <option name="be">Belgium</option>
    <option selected="selected" name="fr">France</option>
    <option name="nl">The Netherlands</option>
  </select>
</div>

If you have any remarks of questions about this, let me know in the comments below.