Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

spatie/laravel-blade-x

Folders and files

NameName
Last commit message
Last commit date

Latest commit

3bda225 · Oct 1, 2018
Oct 1, 2018
Oct 1, 2018
Sep 28, 2018
Sep 28, 2018
Sep 28, 2018
Sep 28, 2018
Sep 28, 2018
Sep 28, 2018
Oct 1, 2018
Sep 28, 2018
Sep 28, 2018
Oct 1, 2018
Oct 1, 2018
Sep 28, 2018

Repository files navigation

Use custom html components in your Blade views

Latest Version on Packagist Build Status Quality Score StyleCI Total Downloads

This package provides an easy way to render custom html components in your Blade views.

Here's an example. Instead of this:

<h1>My view</h1>

@include('myAlert', ['type' => 'error', 'message' => $message])

you can write this

<h1>My view</h1>

<my-alert type="error" :message="$message" />

You can place the content of that alert in a simple blade view that needs to be registered before using the my-alert component.

{{-- resources/views/components/myAlert.blade.php --}}

<div :class="$type">
   {{ $message }}
</div>

Installation

You can install the package via composer:

composer require spatie/laravel-blade-x

The package will automatically register itself.

Usage

The contents of a component can be stored in a simple Blade view.

{{-- resources/views/components/myAlert.blade.php --}}

<div :class="$type">
   {{ $message }}
</div>

Before using that component you must first register it. Typically you would do this in the AppServiceProvider or a service provider of your own

BladeX::component('my-alert', 'components.myAlert')

You can also register an entire directory like this.

// This will register all Blade views that are stored in `resources/views/components`

BladeX::components('components')

In your Blade view you can now use the component using the kebab-cased name:

<h1>My view</h1>

<my-alert type="error" :message="$message" />

Using variables

When using a BladeX component all attributes will be passed as variables to the underlying Blade view.

{{-- the `myAlert` view will receive a variable named `type` with a value of `error` --}}
 
<my-alert type="error">

If you want to pass on a php variable or something that needs to be evaluated you must prefix the attribute name with :.

{{-- the `myAlert` view will receive the contents of `$message` --}}
<my-alert type="error" :message="$message">

{{-- the `myAlert` view will receive the uppercased contents of `$message` --}}
<my-alert type="error" :message="strtoupper($message)">

Using slots

BladeX support slots too. This layout component

{{-- resources/views/components/layout.blade.php --}}

<div>
    <h1>{{ $title }}</h1>
    <div class="flex">
        <div class="w-1/3">
            {{ $sidebar }}
        </div>
        <div class="w-2/3">
            {{ $slot }}
        </div>
    </div>
    <footer>
        {{ $footer }}
    </footer>
</div>

can be used in your views like this:

<layout title="Zed's chopper">
    <slot name="sidebar">
        <ul>
            <li>Home</li>
            <li>Contact</li>
        </ul>
    </slot>

    <main class="content">Whose motorcycle is this?</main>

    <slot name="footer">It's not a motorcycle honey, it's a chopper.</slot>
</layout>

Using view models

Before rendering a BladeX component you might want to transform the passed data. You can do this using a view model. Let's take a look at an example where we are going to render a select element to render 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 itslef, 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 BladeXViewModel
{
    /** @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\BladeXBladeXViewModel. 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>

Prefixing components

If you're using Vue components in combination with BladeX components, it might be worth prefixing your BladeX components to make them easily distinguishable from the rest.

Setting a global prefix can easily be done before or after registering components:

BladeX::component('my-alert', 'components.myAlert');

BladeX::prefix('x');

All your registered components can now be used like this:

<x-my-alert message="Notice the prefix!" />

Under the hood

When you register a component

BladeX::component('my-alert', 'components.myAlert')

with this html

{{-- resources/views/components/myAlert.blade.php --}}
<div class="{{ $type }}">
   {{ $message }}
</div>

and use it in your Blade view like this,

<my-alert type="error" message="{{ $message }}" />

our package will replace that html in your view with this:

@component('components/myAlert', ['type' => 'error','message' => $message,])@endcomponent

After that conversion Blade will compile (and possibly cache) that view.

Because all this happens before any html is sent to the browser, client side frameworks like Vue.js will never see the original html you wrote (with the custom tags).

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

Support us

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Does your business depend on our contributions? Reach out and support us on Patreon. All pledges will be dedicated to allocating workforce on maintenance and new awesome stuff.

License

The MIT License (MIT). Please see License File for more information.