Componentes de plantilla en Laravel

Hace poco, por exigencias del guión, he descubierto los componentes y slots en Laravel. Y tengo que decir que me han resultado una herramienta muy útil para maquetar partes de una web que repiten la misma estructura pero con cambios pequeños.

Concretamente tenía una cabecera con 4 partes: migas de pan, título principal, subtítulo y un botón; pero según la página no había botón o no había migas de pan, o sólo había migas de pan y título principal.

Andaba discurriendo cómo organizar esto, si a tráves de @include y pasándole parámetros o @yield o cómo, y me encontré con la opción de @component y @slot en la documentación de Laravel:
https://laravel.com/docs/5.8/blade#components-and-slots

Conocía el concepto de los slots de Vue pero no sabía que estaban también disponibles en Laravel.

 

Crear el componente

Creamos un componente como una vista más, es un archivo .blade.php como el resto.

Dentro tenemos código html a modo de plantilla para después hacer una llamada @component(‘ruta.al.componente’) y funcionaría igual que si hacemos un @include(‘ruta.a.la.vista’)

<header class="row pagina__cabecera">
    <div class="col-md-9 cabecera__contenido">
        <nav class="cabecera__migas_de_pan" aria-label="breadcrumb">
            <ol class="breadcrumb breadcrumb-style1">
                {{$cabecera_migas_de_pan}}
            </ol>
        </nav>
        
        <h1 class="cabecera__titulo">{{$cabecera_titulo}}</h1>
        <p class="cabecera__subtitulo">{{$cabecera_subtitulo}}</p>
    </div>

    <div class="col-md-3 cabecera__acciones">
        {{$cabecera_acciones}}
    </div>
</header>

Slots dentro del componente

Dentro de nuestro componente podemos interpolar variables mediante dobles llaves. Hasta aquí poco o nada distinto de utilizar un @include

La diferencia reside en que mientras para un @include(‘…’) sólo le pasamos la ruta, para un @component(‘…’) le pasamos la ruta y tenemos directiva de cierre @endcomponent . Entre medias podemos usar @slot(‘variable’) para dar contenido a esas variables interpoladas en la plantilla de nuestro componente:

@section('cabecera')

    @component('distribucion.cabecera.contenedor')
        @slot('cabecera_titulo')
            Issues
        @endslot
        @slot('cabecera_subtitulo')
            From this screen you can check the activities generated by the system and also you can register others
        @endslot
        @slot('cabecera_acciones')
            <button class="btn btn-sm btn-primary">
                <i data-feather="plus-circle" class="wd-10"></i> Añadir nuevo
            </button>
        @endslot
    @endcomponent

@endsection

En mi caso, encerré cada @slot dentro de un @if correspondiente para que si no recibe la variable no se pinte y así, manteniendo la estructura, cargar el componente con los elementos que toquen.

<header class="row pagina__cabecera">
    <div class="col-md-9 cabecera__contenido">
        @if(isset($cabecera_migas_de_pan))
            <nav class="cabecera__migas_de_pan" aria-label="breadcrumb">
                <ol class="breadcrumb breadcrumb-style1">
                    {{$cabecera_migas_de_pan}}
                </ol>
            </nav>
        @endif

        @if(isset($cabecera_titulo))
            <h1 class="cabecera__titulo">{{$cabecera_titulo}}</h1>
        @endif

        @if(isset($cabecera_subtitulo))
            <p class="cabecera__subtitulo">{{$cabecera_subtitulo}}</p>
        @endif

    </div>

    @if(isset($cabecera_acciones))
        <div class="col-md-3 cabecera__acciones">
            {{$cabecera_acciones}}
        </div>
    @endif

</header>

Aunque pueda ser algo más extenso lo veo más cómodo que cargar variables como parámetros en un @include y dentro de los @slot puedo colocar código html tabulado y organizado para que sea más legible.

@section('cabecera')

    @component('distribucion.cabecera.contenedor')
        @slot('cabecera_titulo')
            Issues
        @endslot
        @slot('cabecera_subtitulo')
            From this screen you can check the activities generated 
            by the system and also you can register others
        @endslot
        @slot('cabecera_acciones')
            <button class="btn btn-sm btn-primary">
                <i data-feather="plus-circle" class="wd-10"></i> Añadir nuevo
            </button>
        @endslot
    @endcomponent

@endsection

En la documentación de Laravel se explican algunas posibilidades más como que también se puede pasar parámetros al componente

@component('alert', ['foo' => 'bar'])

O incluso crear una directiva propia para llamarlo

Blade::component('components.alert', 'alert');
@alert
    You are not allowed to access this resource!
@endalert

En este caso yo seguiría la buena práctica de los web components y nombraría a directiva con al menos 2 palabras (p.e. componente-alerta).