6-Componentes en Laravel con el Ejemplo de Gestor de Tarea

  Los componentes en Laravel son una forma poderosa de crear elementos reutilizables en tus vistas. Vamos a refactorizar el gestor de tareas usando componentes.

Tipos de Componentes en Laravel

  1. Componentes de Clase: Más potentes, con lógica propia

  2. Componentes Anónimos: Más simples, solo con vista

1. Refactorizando el Layout con Componentes

Componente Layout Principal (app/View/Components/AppLayout.php)

php
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class AppLayout extends Component
{
    public $titulo;
    
    public function __construct($titulo = 'Gestor de Tareas')
    {
        $this->titulo = $titulo;
    }
    
    public function render()
    {
        return view('layouts.app');
    }
}

Vista del Layout (resources/views/layouts/app.blade.php)

html
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $titulo }}</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
    <x-navbar />
    
    <main class="container mx-auto px-4 py-6">
        {{ $slot }}
    </main>
    
    <x-footer />
</body>
</html>

2. Componente Navbar

Crear el componente

bash
php artisan make:component Navbar

Componente (app/View/Components/Navbar.php)

php
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Navbar extends Component
{
    public function render()
    {
        return view('components.navbar');
    }
}

Vista del componente (resources/views/components/navbar.blade.php)

html
<nav class="bg-blue-600 text-white p-4 shadow-md">
    <div class="container mx-auto flex justify-between items-center">
        <a href="{{ route('tareas.index') }}" class="text-xl font-bold">GestorTareas</a>
        
        <div class="space-x-4">
            <a href="{{ route('tareas.index') }}" class="hover:underline">Inicio</a>
            <a href="{{ route('tareas.crear') }}" class="hover:underline">Crear Tarea</a>
            <a href="{{ route('salir') }}" class="hover:underline">Salir</a>
        </div>
    </div>
</nav>

3. Componente Footer

bash
php artisan make:component Footer

Vista del componente (resources/views/components/footer.blade.php)

html
<footer class="bg-gray-800 text-white py-6 mt-8">
    <div class="container mx-auto text-center">
        <p>Sistema de Gestión de Tareas &copy; {{ date('Y') }}</p>
    </div>
</footer>

4. Componente para Mostrar Tareas

Crear componente de tarjeta de tarea

bash
php artisan make:component TareaCard --view

Vista del componente (resources/views/components/tarea-card.blade.php)

html
<div @class([
    'border rounded-lg p-4 mb-4 shadow-sm',
    'bg-green-50 border-green-200' => $tarea['completada'],
    'bg-yellow-50 border-yellow-200' => !$tarea['completada']
])>
    <h3 class="text-lg font-semibold">{{ $tarea['titulo'] }}</h3>
    <p class="text-gray-600 my-2">{{ $tarea['descripcion'] }}</p>
    
    <div class="flex justify-end space-x-2 mt-4">
        <x-boton 
            href="{{ route('tareas.mostrar', $tarea['id']) }}" 
            color="blue">
            Ver
        </x-boton>
        
        <x-boton 
            href="{{ route('tareas.eliminar', $tarea['id']) }}" 
            color="red">
            Eliminar
        </x-boton>
    </div>
</div>

5. Componente de Botón Reutilizable

Crear componente

bash
php artisan make:component Boton --view

Vista del componente (resources/views/components/boton.blade.php)

php
@props(['href' => null, 'color' => 'gray'])

@php
    $colors = [
        'blue' => 'bg-blue-600 hover:bg-blue-700',
        'red' => 'bg-red-600 hover:bg-red-700',
        'green' => 'bg-green-600 hover:bg-green-700',
        'gray' => 'bg-gray-600 hover:bg-gray-700'
    ];
@endphp

@if($href)
    <a href="{{ $href }}" @class([
        'px-4 py-2 rounded text-white font-medium',
        $colors[$color] ?? $colors['gray']
    ])>
        {{ $slot }}
    </a>
@else
    <button @class([
        'px-4 py-2 rounded text-white font-medium',
        $colors[$color] ?? $colors['gray']
    ])>
        {{ $slot }}
    </button>
@endif

6. Refactorizando las Vistas con Componentes

Vista Index (resources/views/tareas/index.blade.php)

html
<x-app-layout titulo="Lista de Tareas">
    <div class="flex justify-between items-center mb-6">
        <h1 class="text-2xl font-bold">Lista de Tareas</h1>
        <x-boton href="{{ route('tareas.crear') }}" color="green">
            Nueva Tarea
        </x-boton>
    </div>

    @if(count($tareas) > 0)
        <div class="space-y-4">
            @foreach($tareas as $tarea)
                <x-tarea-card :tarea="$tarea" />
            @endforeach
        </div>
    @else
        <div class="bg-white p-6 rounded-lg shadow text-center">
            <p class="text-gray-600">No hay tareas registradas.</p>
        </div>
    @endif
</x-app-layout>

Vista Crear Tarea (resources/views/tareas/crear.blade.php)

html
<x-app-layout titulo="Crear Nueva Tarea">
    <div class="max-w-md mx-auto bg-white p-6 rounded-lg shadow">
        <h2 class="text-xl font-semibold mb-4">Crear Nueva Tarea</h2>
        
        <form action="{{ route('tareas.guardar') }}" method="POST">
            @csrf
            
            <div class="mb-4">
                <label for="titulo" class="block text-gray-700 mb-2">Título</label>
                <input type="text" id="titulo" name="titulo" required
                       class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
            </div>
            
            <div class="mb-4">
                <label for="descripcion" class="block text-gray-700 mb-2">Descripción</label>
                <textarea id="descripcion" name="descripcion" rows="3"
                          class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
            </div>
            
            <div class="flex justify-end space-x-3">
                <x-boton type="submit" color="blue">Guardar</x-boton>
                <x-boton href="{{ route('tareas.index') }}" color="gray">Cancelar</x-boton>
            </div>
        </form>
    </div>
</x-app-layout>

Vista Mostrar Tarea (resources/views/tareas/mostrar.blade.php)

html
<x-app-layout titulo="Detalles de Tarea">
    <div class="max-w-2xl mx-auto bg-white p-6 rounded-lg shadow">
        <div @class([
            'border-l-4 p-4 mb-6',
            'border-green-500 bg-green-50' => $tarea['completada'],
            'border-yellow-500 bg-yellow-50' => !$tarea['completada']
        ])>
            <h2 class="text-2xl font-bold">{{ $tarea['titulo'] }}</h2>
            <p class="text-gray-700 mt-2">{{ $tarea['descripcion'] }}</p>
            
            <div class="mt-4 flex items-center">
                <span @class([
                    'px-2 py-1 rounded-full text-xs font-medium',
                    'bg-green-200 text-green-800' => $tarea['completada'],
                    'bg-yellow-200 text-yellow-800' => !$tarea['completada']
                ])>
                    {{ $tarea['completada'] ? 'Completada' : 'Pendiente' }}
                </span>
            </div>
        </div>
        
        <div class="flex justify-end">
            <x-boton href="{{ route('tareas.index') }}" color="gray">Volver</x-boton>
        </div>
    </div>
</x-app-layout>

Ventajas de este Enfoque con Componentes

  1. Reutilización de código: Los componentes como <x-boton> se usan en múltiples lugares

  2. Consistencia visual: Todos los botones y tarjetas tienen el mismo estilo

  3. Mantenibilidad: Cambios en un componente se reflejan en toda la aplicación

  4. Organización: Código mejor estructurado y separado por responsabilidades

  5. Personalización: Los componentes aceptan props para modificar su comportamiento

Características implementadas:

  • Layout principal con componentes para navbar y footer

  • Componente reutilizable de botón con variantes de color

  • Componente de tarjeta de tarea con lógica condicional de clases

  • Sistema de slots para contenido dinámico

  • Props para personalizar componentes

  • Conditional classes con @class directive

Este enfoque con componentes hace que la aplicación sea más escalable y mantenible, siguiendo las mejores prácticas de Laravel y Blade.

Entendiendo los Slots en los Componentes de Laravel Blade

Los slots son una característica fundamental de los componentes en Laravel Blade que permiten inyectar contenido dinámico dentro de tus componentes. Son similares a los "children" en otros sistemas de componentes.

Conceptos Básicos de Slots

1. Slot Principal ($slot)

El slot principal es el contenido que se pasa entre las etiquetas de apertura y cierre del componente.

Ejemplo de uso:

html
<x-alert>
    Este es el contenido que irá en el slot principal
</x-alert>

En el componente (resources/views/components/alert.blade.php):

html
<div class="alert">
    {{ $slot }} <!-- Aquí se renderizará el contenido -->
</div>

2. Slots Nombrados

Permiten tener múltiples áreas de contenido en un componente.

Ejemplo de uso:

html
<x-card>
    <x-slot name="header">
        Título de la tarjeta
    </x-slot>

    Contenido principal de la tarjeta

    <x-slot name="footer">
        Pie de la tarjeta
    </x-slot>
</x-card>

Componente card.blade.php:

html
<div class="card">
    <div class="card-header">
        {{ $header }}
    </div>

    <div class="card-body">
        {{ $slot }} <!-- Slot principal -->
    </div>

    <div class="card-footer">
        {{ $footer }}
    </div>
</div>

Ejemplo Práctico con el Gestor de Tareas

1. Componente Layout con Slots

Componente app-layout (resources/views/layouts/app.blade.php):

html
<!DOCTYPE html>
<html>
<head>
    <title>{{ $title ?? 'Gestor de Tareas' }}</title>
</head>
<body>
    <x-navbar />
    
    <main class="container">
        <!-- Slot principal para el contenido específico de cada página -->
        {{ $slot }}
    </main>
    
    <x-footer>
        <!-- Slot con valor por defecto -->
        <x-slot name="copyright">
            &copy; {{ date('Y') }} Gestor de Tareas
        </x-slot>
    </x-footer>
</body>
</html>

2. Uso del Layout en una Vista

resources/views/tareas/index.blade.php:

html
<x-app-layout title="Lista de Tareas">
    <!-- Este contenido va al slot principal -->
    <h1>Mis Tareas</h1>
    
    @foreach($tareas as $tarea)
        <x-tarea-card :tarea="$tarea">
            <!-- Slot adicional para acciones -->
            <x-slot name="acciones">
                <x-boton href="{{ route('tareas.mostrar', $tarea['id']) }}">
                    Ver Detalles
                </x-boton>
            </x-slot>
        </x-tarea-card>
    @endforeach
</x-app-layout>

3. Componente TareaCard con Slots

resources/views/components/tarea-card.blade.php:

html
<div class="tarea {{ $tarea['completada'] ? 'completada' : 'pendiente' }}">
    <h3>{{ $tarea['titulo'] }}</h3>
    <p>{{ $tarea['descripcion'] }}</p>
    
    <!-- Slot para contenido adicional -->
    @isset($acciones)
        <div class="acciones">
            {{ $acciones }}
        </div>
    @endisset
    
    <!-- Slot principal para cualquier otro contenido -->
    {{ $slot }}
</div>

Tipos de Slots Avanzados

1. Slots con Valores por Defecto

Puedes definir valores por defecto para los slots:

html
@props(['title' => 'Título por defecto'])

<h2>{{ $title }}</h2>
{{ $slot }}

2. Slots Escopados

Pasas datos al slot como si fuera un componente:

html
<x-table :$users>
    <x-slot:header column="nombre">
        Nombre del Usuario
    </x-slot:header>
</x-table>

En el componente:

html
@props(['users'])

<table>
    <thead>
        <tr>
            {{ $header(['column' => 'nombre']) }}
        </tr>
    </thead>
    <!-- ... -->
</table>

Buenas Prácticas con Slots

  1. Usa slots nombrados para contenido complejo con múltiples secciones

  2. Mantén los slots simples - si necesitas mucha lógica, considera dividir en más componentes

  3. Documenta los slots disponibles en componentes complejos con comentarios

  4. Usa valores por defecto para hacer componentes más flexibles

  5. Considera slots escopados cuando necesites pasar datos al contenido del slot

Los slots son una herramienta poderosa en Laravel Blade que te permite crear componentes altamente reutilizables y flexibles, manteniendo un código limpio y organizado

Comentarios

Entradas más populares de este blog

0-Sistema de Tareas

13-CRUD en Laravel para Web y API con Rutas Normales (No Resource)

10-Introducción a Blade en Laravel con un Ejemplo Práctico