Extra: Componentes de Laravel Livewire Volt, Listado paginado
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