Extra: Componentes de Laravel Livewire Volt, Listado paginado

Video thumbnail

Es muy facil convertir un componente de Livewire clásico, es decir, una clase y vista a un componente de volt, partiremos de:

resources/views/livewire/dashboard/category/index.blade.php

<div>

    <x-action-message on="deleted">
        <div class="box-action-message">
            {{ __("Category delete success") }}
        </div>
    </x-action-message>

    <flux:heading>{{ __('Category List') }}</flux:heading>
    <flux:text class="mt-2">Lorem ipsum dolor sit amet consectetur adipisicing.</flux:text>

    <div class="separation"></div>

    <flux:button class="ml-1 mb-3" variant='primary' icon="plus" href="{{ route('d-category-create') }}">
        {{ __('Create') }}
    </flux:button>

    <!-- <flux:modal.trigger name="delete-category">
       <flux:button>Delete</flux:button>
    </flux:modal.trigger> -->

    <flux:modal name="delete-category">
        <div class="m-1">
            <flux:heading>{{ __('Delete Category') }}</flux:heading>
            <flux:text class="mt-2">{{ __('Are you sure you want to delete this category?') }}</flux:text>

          <div class="flex flex-row-reverse">
            <flux:button class="mt-4" variant='danger' icon="trash" wire:click="delete()">
                {{ __('Delete') }}
            </flux:button>
          </div>
        </div>
    </flux:modal>

    <div class="overflow-x-auto shadow-md rounded-lg">
        <table class="table w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
            <thead class="rounded-lg text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
                <tr>
                    <th>
                        Id
                    </th>
                    <th>
                        Title
                    </th>
                    <th>
                        Actions
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach ($categories as $c)
                <tr
                    class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700 border-gray-200">
                    <td>
                        {{ $c->id }}
                    </td>
                    <td>
                        {{ $c->title }}
                    </td>
                    <td>
                        <a href="{{ route('d-category-edit', $c) }}">Edit</a>

                        <flux:modal.trigger wire:click="selectCategodyToDelete({{ $c }})" name="delete-category">
                            <flux:button class="ml-3" variant='danger' size="xs">{{ __('Delete') }}</flux:button>
                        </flux:modal.trigger>

                        <!-- <flux:button class="ml-3" variant='danger' size="xs" wire:click="delete({{ $c }})">
                            {{ __('Delete') }}
                        </flux:button> -->
                    </td>
                </tr>
                @endforeach


            </tbody>
        </table>
    </div>
    <br>
    {{ $categories->links() }}
</div>

app/Livewire/Dashboard/Category/Index.php

<?php

namespace App\Livewire\Dashboard\Category;

use Livewire\Component;
use Livewire\WithPagination;

use App\Models\Category;
use Flux\Flux;

class Index extends Component
{
    use WithPagination;

    public $categoryToDelete;

    public function render()
    {
        $categories = Category::paginate(10);
    
        return view('livewire.dashboard.category.index', compact('categories'));
    }

    function selectCategodyToDelete(Category $category){
        $this->categoryToDelete = $category;
    }

    function delete(){
        $this->dispatch("deleted");
        Flux::modal("delete-category")->close();
        $this->categoryToDelete->delete();
    }
}

Y un componente de Volt es exactamente lo mismo:

$ php artisan make:volt Volt/Dashboard/Category/Index
<?php

use Livewire\Volt\Component;

use Illuminate\Support\Facades\Storage;
use Livewire\WithFileUploads;

use App\Models\Category;

new class extends Component {

    use WithFileUploads;

    public $title;
    public $slug;
    public $text;
    public $image;

    protected $rules = [
        'title' => "required|min:2|max:255",
        'text' => "nullable",
        'image' => "nullable|image|max:1024",
    ];

    public $category;

    public function mount(?int $id = null)
    {
        if ($id != null) {
            $this->category = Category::findOrFail($id);
            $this->title = $this->category->title;
            $this->text = $this->category->text;
        }
    }

    function submit()
    {
        // validate
        $this->validate();

        if ($this->category) {
            $this->category->update([
                'title' => $this->title,
                'text' => $this->text,
            ]);
            $this->dispatch("updated");
        } else {

            $this->category = Category::create([
                'title' => $this->title,
                'slug' => str($this->title)->slug(),
                'text' => $this->text,
            ]);
            $this->dispatch("created");
        }

        // upload
        if ($this->image) {
            // delete old img
            if ($this->category->image) {
                Storage::disk('public_upload')
                    ->delete('images/category/' . $this->category->image);
            }


            $imageName = $this->category->slug . '.' . $this->image->getClientOriginalExtension();
            $this->image->storeAs('images/category', $imageName, 'public_upload');


            $this->category->update([
                'image' => $imageName
            ]);
        }
    }

}; ?>

<div>

    <x-action-message on="created">
        <div class="box-action-message">
            {{ __('Created category success') }}
        </div>

    </x-action-message>

    <x-action-message on="updated">
        <div class="box-action-message">
            {{ __('Updated category success') }}
        </div>
    </x-action-message>

    <flux:heading>

        @if ($category)
            {{ __('Category edit: ') }} <span class="font-bold">{{ $category->title }}</span>
        @else
            {{ __('Category create') }}
        @endif
    </flux:heading>
    <flux:text class="mt-2">Lorem ipsum dolor sit amet consectetur adipisicing.</flux:text>

    <div class="separation"></div>

    <form wire:submit.prevent="submit" class="flex flex-col gap-4">
        <!-- <input type="text" wire:model="title">
        <textarea wire:model="text"></textarea>
        <button type="submit" wire:click="submit">Send</button> -->

        {{-- <input type="text" wire:model="title"> --}}

        {{-- @error('title')
        {{ $message }}
        @enderror --}}

        <flux:input wire:model="title" :label="__('Title')" />

        <flux:textarea wire:model="text" :label="__('Text')" />

        <flux:input wire:model="image" type='file' :label="__('Image')" />

        {{-- @error('text')
        {{ $message }}
        @enderror --}}

        <div>
            <flux:button variant="primary" type="submit">
                {{ __('Save') }}
            </flux:button>
        </div>
    </form>

    @if ($category && $category->image)
        <img class="w-40 my-3" src="{{ $category->getImageUrl() }}" />
    @endif

</div>

Creamos la ruta:

 // demo volt
   Volt::route('volt', 'volt.dashboard.category.index')->name('volt-d-category-index');

Como puedes ver, removimos el método de render y el caso de la paginación es especial, debemos de usar el método de with() y retornar los datos a la vista.

Acepto recibir anuncios de interes sobre este Blog.

Haremos una demo de como usar Volt convirtiendo el listado de Laravel Livewire clásico a Volt.

- Andrés Cruz

In english