Qué son los componentes de Laravel Blade, también conocidos como "x-components". Cómo crear y usar componentes en las vistas de Laravel. Qué posibilidades tenemos con los componentes anónimos.
Una de las novedades de las últimas versiones de Laravel, en lo que respecta al lenguaje de templates Blade, son los componentes. Aparecieron en la versión 7 de Laravel y nos ofrecen una manera mucho más potente de organizar el código de las vistas y realizar la composición de los elementos de la página de una manera más sencilla y reutilizable.
Para comenzar a abordar este tema debemos aclarar primero que existen existen dos tipos de componentes en Laravel Blade:
- Componentes: los cuales están basados en clases para funcionar. Para construirlos necesitamos una clase (de programación orientada a objetos) y un archivo con la vista del componente.
- Componentes anónimos: los cuales solamente tienen un archivo de vista y no requieren la creación de una clase para poder implementarlos.
Aunque en la documentación de Laravel comienzan explicando cómo realizar componentes basados en clase, nosotros vamos a hacerlo explicando primero los componentes anónimos, pues son los más sencillos y además los más utilizados.
Un componente de Laravel es como un componente web, pero que se crea en el lado del backend, simplemente con el objetivo de organizar el código de las vistas y permitir una mejor composición del HTML de la página que se va a generar.
Todos los componentes que generemos en Laravel generarán una etiqueta nueva que podremos usar en los templates de Blade. Todas las etiquetas comienzan por una "x", como <x-message>
, <x-button>
, <x-select>
o cualquier cosa que podamos necesitar. A veces nos podemos referir a éstos como "x components", justamente por comenzar siempre por una "x".
Es importante que quede claro que estos componetes no llegarán al navegador. Es decir, se renderizan en HTML que se enviará luego al navegador. Por tanto, es algo que quedará en propio de Laravel, de modo que una vez ejecutada la página se convertirán siempre en HTML plano.
Cómo hacer componentes anónimos de Blade
Para hacer un componente anónimo simplemente necesitamos crear un archivo con una vista de Blade en una carpeta en particular, llamada "components" que está dentro del directorio de las vistas. Con ello Laravel es capaz de descubrirlos y nosotros podemos usarlos libremente, sin necesidad de declararlos en ninguna parte.
La carpeta donde se colocan los componentes anónimos está en resources/views/components
. Allí colocaremos archivos de blade, como por ejemplo message.blade.php
.
El código de una vista del componente puede ser tan sencillo como un simple HTML.
Mediante este contenido podriamos definir el componente "message", que colocaríamos en la ruta resources/views/components/message.blade.php
.
<p>Esta es la implementación de un componente de Laravel Blade cuyo nombre será x-message</p>
Normalmente los componentes podrán trabajar con datos y mostrar esos datos entre su contenido. En este componente no lo estamos haciendo, por lo que sería un simple párrafo con un texto fijo, pero obviamente se pueden conseguir cosas más dinámicas, como veremos enseguida.
Cómo usar componentes anónimos
Para usar el componente simplemente colocamos la etiqueta "x-", seguido del nombre del componente, que se infiere del nombre del archivo blade que lo implementa. Si nuestro componente se llama message.blade.php, el nombre del componente será x-message y lo usaremos así.
<x-message></x-message>
Cuando se renderice esta vista, se sustituirá la etiqueta x-message
por el contenido de la vista del componente.
Organización de los componentes por carpetas
Hay veces que es necesario organizar los componentes que tenemos en la carpeta views/components
, especialmente cuando tenemos muchos, para que nos resulte más sencilla su localización. En estos casos puedes crear subdirectorios y colocar los componentes ahí.
Por ejemplo podemos colocar componentes en la carpeta views/components/invoice
o views/components/product
para temas que tengan que ver con facturas o productos.
A la hora de acceder a los componentes que tienes en carpetas tienes que usar una notación especial con un punto para dividir los subdirectorios donde has guardado el componente. Por ejemplo, para un componente almacenado en la carpeta views/components/pdf/header
usarás la siguiente etiqueta:
<x-pdf.header></x-pdf.header>
Enviando datos al componente
Podemos enviar datos a los componentes de diversas maneras. La más sencilla sería mediante atributos que colocaremos cuando usemos el componente.
<x-message message="Bienvenidos a Laravel" date="febrero 2022"></x-message>
En este caso estamos enviando dos atributos (message y date), cuyos valores podremos usar dentro del componente, tal como usamos variables comunes de Blade. El nombre del atributo será el nombre de la variable que tendríamos dentro del componente, en este caso $message
y $date
.
<p class="bg-blue-800 p-4 text-lg text-red-100">
{{ $message }} - {{ $date }}
</p>
Caso especial de envío de datos al componente que están en variables del template
Si los datos que queremos enviar al componente están dentro de variables PHP que tengamos en un template, la notación cambiará un poco.
<x-pdf-header :company="$company" :invoice="$invoice"></x-pdf-header>
En este caso estamos enviando al componente dos variables que tenemos en el template. Para ello anteponemos el caracter dos puntos (:) antes del nombre del atributo, lo que nos permite mencionar las variables de PHP que queremos enviar.
Attribute bag del componente
Además de acceder a los valores de los atributos como variables, también los podemos usar en lo que Laravel denomina el "attribute bag". Para ello usamos la variable $attributes dentro del componente.
<p {{ $attributes }}>
{{ $message }} - {{ $date }}
</p>
El "attribute bag" es una estructura que nos genera Laravel donde va agregando todos los atributos enviados al usar el componente que no hayan declarado como propiedades. Enseguida veremos cómo declarar atributos como propiedades con la directiva @props, pero ya adelantamos que, si no declaramos nada en @props, todos los atributos formarán parte del "attribute bag".
La bolsa de atributos la podemos usar para traspasar los atributos que indicamos en el componente hacia atributos dentro del template. Por ejemplo, al usar un componente de esta manera:
<x-button class="bg-red-600 text-red-100" id="danger"></x-button>
Los atributos "class" e "id" que hemos indicado al usar el componente los podremos trasladar a una o varias de las etiquetas del template con $attributes.
<button {{ $attributes }}>Soy un botón</button>
Este produciría un renderizado del botón con el siguiente HTML de resultado, en el que vemos que aparecen tal cual en la etiqueta <button>
.
<button class="bg-red-600 text-red-100" id="danger">Soy un botón</button>
Cómo combinar atributos del attribute bag
Además de colocar los atributos del "attribute bag" tal cual aparecen en la etiqueta de uso del componente, también podemos combinar datos con otros que el propio componente pueda definir.
Para ello usamos el método merge
de $attributes
, indicando en un array asociativo todos los datos que se deben mezclar.
<button {{ $attributes->merge(['class' => 'button']) }} >Soy un botón</button>
Ante el anterior template, el HTML resultado de usar el componente sería el siguiente.
<button class="button bg-red-600 text-red-100" id="danger">Soy un botón</button>
Como puedes ver, es igual que antes, agregando una nueva clase "button" que estará siempre presente, junto con los valores del class que se hayan entregado en el attribute bag.
Directiva @props
Como hemos visto, los datos que hemos enviado en atributos al usar el componente estarán presentes en dos partes:
- Como variables en el componente
- Como atributos en $attributes (attribute bag)
Sin embargo, no es muy común necesitar que los atributos de la etiqueta del componente estén en dos lugares distintos. De hecho, generalmente los querremos en uno u otro lugar.
Para decidir cuáles de estos atributos deben formar parte de los datos del componente y por tanto no formar parte de $attributes
, usamos @props
.
La declaración @props
la colocamos arriba del todo en el archivo de la vista del componente, de esta manera:
@props(['message', 'date'])
<p>
{{ $message }} - {{ $date }}
</p>
Todos los atributos declarados en @props
se encontrarán como datos del componente y no como "attribute bag". Además mediante @props
puedes asignar valores predeterminados a los atributos, de esta manera.
@props(['message', 'date' => '2022'])
Como puedes ver, no es necesario que le indiquemos valores predeterminados para todos los atributos si no lo necesitas.
Laravel Blade mostrará un error cuando faltan datos para la ejecución del componente. Es decir, cualquier dato que falte para renderizar el template nos arrojará un fallo. Gracias a los valores predeterminados conseguiremos que el componente no falle si ese dato no se llega a indicar.
Slots
Como hemos visto, podemos pasar datos al componente por medio de atributos. Sin embargo, los atributos que podemos indicar en la etiqueta del componente podrán tener valores sencillos, generalmente cortos, sin saltos de línea y cosas similares.
En muchas ocasiones queremos enviar al componente una mayor cantidad de información para que se renderice dentro, lo que podría incluir texto con varias líneas y un marcado de etiquetas tan complejo como fuera necesario. En estos casos es idóneo usar los slots.
Un slot es como un depósito donde se colocará un HTML que tengamos como hijo dentro de la etiqueta de uso del componente. Los slots se pueden usar tanto en componentes anónimos como en componentes basados en clases.
Al usar la etiqueta del componente, todo el contenido que se coloque como hijo será un slot. Ese contenido puede ser algo tan sencillo como un texto, pero también podría ser algo mucho más complejo con muchas etiquetas e incluso otros componentes.
Un ejemplo sencillo de cómo usar un componente que tiene un slot sería este:
<x-button>Clic aquí</x-button>
El texto del interior del componente "Clic aquí" lo podemos reutilizar dentro del template, con la variable $slot
, de esta manera:
<button class="p-2 bg-yellow-200">{{ $slot }}</button>
Esto nos permitiría tener botones con distintos textos, incluso con textos que además incluyan otras etiquetas o elementos HTML.
<x-button>Otro botón</x-button>
<x-button>Algo con <b>negrita</b></x-button>
Múltiples slots
Muchas veces necesitamos hacer componentes que tengan varios slots, por ejemplo un componente de tarjeta que tenga un título y un cuerpo. Cuando queremos usar varios slots, para poder distinguir el contenido que irá en cada uno de ellos, usamos la etiqueta <x-slot>
dentro del contenido del componente.
<x-card>
<x-slot:title>
Un titular
</x-slot>
<p>Esto es el cuerpo</p>
<p>Esto continúa siendo el cuerpo</p>
</x-card>
Cada <x-slot>
tendrá un nombre. En este caso lo hemos llamado "title" y lo puedes ver en <x-slot:title>
. El ejemplo anterior contiene dos slots, uno con nombre, llamado "title" y otro slot sin nombre.
Todo contenido del componente que no se haya colocado dentro de una etiqueta <x-slot>
se irá al slot princial o predeterminado, aquel "sin nombre".
Al crear el template del componente podremos usar ambos slots de esta manera:
<div class="shadow-lg rounded-lg p-8 bg-white">
<h2 class="text-xl font-bold mb-3">{{ $title }}</h2>
<div>{{ $slot }}</div>
</div>
Como puedes ver, el slot con nombre lo conseguimos usar a partir de su propio nombre, en este caso $title
. El slot que no tiene nombre lo seguimos usando a través de la variable $slot
.
Atributos de los slots
A los slots también les podemos poner atributos para utilizar sus valores desde el template del componente anónimo.
Los atributos funcionan de manera similar a como hemos descrito anteriormente para los componentes en general, solo que se los colocamos en la etiqueta x-slot
:
<x-slot:title class="border border-solid border-red-500">
Un titular
</x-slot>
Esos atributos los encontraremos en el código del componente, mediante la propia variable generada para reutilizar el slot, en este caso $title
, dado que el slot se llama x-slot:title
. Esa variable contiene un objeto, con una propiedad habilitada para acceder a sus attribute bag.
En este código estamos reutilizando el slot anterior, por medio de $title->attributes
.
<div class="shadow-lg rounded-lg p-8 bg-white">
<h2 {{ $title->attributes->merge(['class' => 'text-xl font-bold mb-3']) }}>{{ $title }}</h2>
<div>{{ $slot }}</div>
</div>
Conclusión
Esto es todo lo que necesitas saber para comenzar a usar componentes anónimos, los cuales son muy útiles y te permiten mejorar las prestaciones del código de las vistas con Blade. Los componentes han venido para quedarse y en la documentación de Laravel se ven bastante, así como en los scaffolding de Laravel como Jetstream o Breeze.
En los próximos artículos podemos ver ejemplos más complejos de componentes que nos ayudarán a entender sus elevadas capacidades de reutilización.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...