MedicOneSystems / livewire-datatables

Advanced datatables using Laravel, Livewire, Tailwind CSS and Alpine JS
https://livewire-datatables.com/
MIT License
1.19k stars 258 forks source link

How to manage row edit (not inline) #475

Closed robman70 closed 2 years ago

robman70 commented 2 years ago

Hi, I'm completely stuck: I'm trying to manage row edit, based on the https://livewire-datatables.com/actions example. Could someone show me a simple, basic, example of editing a row using a modal form? An example based on the standard users table would be fine, even if only with the email field.

Thanks a lot

robman70 commented 2 years ago

Sorry... solved by myself. I'm pretty new to Laravel, so I'm still a bit confused

hakimj96 commented 2 years ago

hi, can you share your ways of solving it? seems I am stuck here as well

robman70 commented 2 years ago

Of course. I didn't do this because, being new to Laravel, I thought it was a silly issue.

The work is still at the beginning, for example I would like to implement the Livewire confirmation dialogs (e.g.: before deletion) but it seems stupid to me to have to create one for each form (maybe I haven't quite understood how they work yet), so I'm looking for a solution to create a reusable and parameterizable one and in the meantime I used a trivial Confirm () in js.

I have removed some unnecessary parts for the purpose of this example, I hope everything works fine.

Here is the code... feel free to suggest better solutions.

Http/Livewire/Users.php

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\User;
use Illuminate\Support\Str;
use Mediconesystems\LivewireDatatables\Column;
use Mediconesystems\LivewireDatatables\NumberColumn;
use Mediconesystems\LivewireDatatables\DateColumn;
use Mediconesystems\LivewireDatatables\Http\Livewire\LivewireDatatable;

class Users extends LivewireDatatable
{
    public $name, $email, $user_id;
    public $isModalOpen = 0;
    public $model = User::class;

    public function columns()
    {
        return [
            NumberColumn::name('id')->filterable(),

            Column::name('name')->label(__('Name'))->filterable()->searchable()->editable(),
            Column::name('email')->label(__('email'))->truncate()->filterable()->searchable()->editable(),

            DateColumn::name('created_at')->label(__('Created at'))->filterable(),

            Column::callback(['id', 'name', 'email'], function ($id, $name, $email) {
            $isModalOpen = $this->isModalOpen;
            return view('livewire.users.datatable-actions', ['id' => $id, 'name' => $name, 'email' => $email, 'isModalOpen' => $isModalOpen]);
        })->unsortable()
        ];
    }
    public function create()
    {
        $this->resetCreateForm();
        $this->openModalPopover();
    }
    public function openModalPopover()
    {
        $this->isModalOpen = true;
    }
    public function closeModalPopover()
    {
        $this->isModalOpen = false;
    }
    private function resetCreateForm()
    {
        $this->name = '';
        $this->email = '';
    }

    public function store()
    {
        $this->validate([
            'name' => 'required',
            'email' => 'required',
        ]);

        User::updateOrCreate(['id' => $this->user_id], [
            'name' => $this->name,
            'email' => $this->email,
        ]);
        session()->flash('message', ($this->user_id ? __('Data updated') : __('Creation complete')));
        $this->closeModalPopover();
        $this->resetCreateForm();
    }

    public function edit($id)
    {
        $user = User::findOrFail($id);
        $this->user_id = $id;
        $this->name = $user->name;
        $this->email = $user->email;

        $this->openModalPopover();
    }

    public function delete($id)
    {
        User::find($id)->delete();
        session()->flash('message', __('Deletion complete'));
    }

    public function remove($id)
    {
        $user = User::findOrFail($id);
        $user->deleted = true;
        $user->save();
        session()->flash('message', __('Removal complete'));
    }

}

views/livewire/users/index.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Users') }}
        </h2>
    </x-slot>
    <div class="py-10 space-y-8">
        @if (session()->has('message'))
        <div class="bg-teal-100 border-t-4 border-teal-500 rounded-b text-teal-900 px-4 py-3 shadow-md my-3" role="alert">
            <div class="flex">
                <div>
                    <p class="text-sm">{{ session('message') }}</p>
                </div>
            </div>
        </div>
        @endif
        <livewire:users />
    </div>
</x-app-layout>

views/livewire/users/datatable-actions.blade.php

<div class="flex space-x-1 justify-around">
    <a href="{{ route('users', [$name]) }}" target="_blank" class="p-1 text-teal-600 hover:bg-teal-600 hover:text-white rounded">
        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
            <path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path>
            <path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path>
        </svg>
    </a>
    <a wire:click="edit({{ $id }})" class="flex px-3 py-2 text-gray-800 cursor-pointer" title="{{ __('Edit') }}"><i class="fa-solid fa-pencil"></i></a>
    <a wire:click="remove({{ $id }})" class="flex px-3 py-2 text-red-100 cursor-pointer" title="{{ __('Delete') }}" onclick="confirm('Confirm?') || event.stopImmediatePropagation()"><i class="fa-solid fa-trash-can"></i></a>

    <x-slot name="trigger">
    </x-slot>
    @if($isModalOpen)
    @include('livewire.users.create')
    @endif
</div>

views/livewire/create.blade.php

<div class="fixed z-10 inset-0 overflow-y-auto ease-out duration-400">
    <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
        <div class="fixed inset-0 transition-opacity">
            <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
        </div>
        <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" role="dialog" aria-modal="true" aria-labelledby="modal-headline">
            <form>
                <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                    <div class="">
                        <div class="mb-4">
                            <label for="name" class="block text-gray-700 text-sm font-bold mb-2">Name:</label>
                            <input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="name" wire:model="name" value="{{ $name }}" placeholder="Enter Name">
                            @error('name') <span class="text-red-500">{{ $message }}</span>@enderror
                        </div>
                        <div class="mb-4">
                            <label for="email" class="block text-gray-700 text-sm font-bold mb-2">E-Mail:</label>
                            <input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="email" wire:model="email" value="{{ $email }}" placeholder="Enter email">
                            @error('email') <span class="text-red-500">{{ $message }}</span>@enderror
                        </div>
                    </div>
                </div>
                <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
                    <span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
                        <button wire:click.prevent="store()" onclick="confirm('Confirm?') || event.stopImmediatePropagation()" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-bold text-white shadow-sm hover:bg-red-700 focus:outline-none focus:border-green-700 focus:shadow-outline-green transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                            Store
                        </button>
                    </span>
                    <span class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
                        <button wire:click="closeModalPopover()" type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-base leading-6 font-bold text-gray-700 shadow-sm hover:text-gray-700 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                            Close
                        </button>
                    </span>
                </div>
            </form>
        </div>
    </div>
</div>