Hello, I have run into an issue.
I have looped modals for each user that is displayed like so:
@foreach ($this->users as $user)
<flux:modal name="edit-user-{{ $user->id }}" class="w-full space-y-6">
@include('livewire.admin.user-management.form')
</flux:modal>
<flux:modal name="delete-profile-{{ $user->id }}" class="min-w-[22rem] space-y-6">
<div>
<flux:heading size="lg">Delete User?</flux:heading>
<flux:subheading>
<p>You're about to delete the user: {{ $user->name }}.</p>
<p>This action cannot be reversed.</p>
</flux:subheading>
</div>
<div class="flex gap-2">
<flux:spacer />
<flux:modal.close>
<flux:button variant="ghost">Cancel</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="danger" wire:click="delete({{ $user->id }})">Delete User</flux:button>
</div>
</flux:modal>
@endforeach
My issue comes when I click onto another page. Once I load onto another page I am unable to open a model to edit/confirm a delete.
I'm sure it has something to do with the rendering of a volt component and the need to add a listener, but I am unsure.
I'll post my code below (documented for ease)
<?php
/**
* User Management Component
* Handles user listing, adding, editing, and deleting operations.
*/
use Illuminate\Validation\Rules;
use Spatie\Permission\Models\Role;
use App\Models\User;
use Livewire\Volt\Component;
use Livewire\WithPagination;
use Livewire\Attributes\{Url, Validate};
new class extends Component {
use WithPagination;
#[URL]
public $sortBy = 'id';
#[URL]
public $sortDirection = 'asc';
public $userId = '';
public $name = '';
public $email = '';
public $username = '';
public $password = '';
public $password_confirmation = '';
public $roles = [];
public $selectedRoles = [];
/**
* Initializes the component, loading roles.
*/
public function mount()
{
$this->roles = Role::all();
}
/**
* Sorts users by specified column.
* @param string $column
*/
public function sort($column)
{
$this->sortBy === $column
? $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'
: $this->sortBy = $column;
$this->sortDirection = 'asc';
}
/**
* Validation rules for user data.
* @param User $user
* @return array
*/
public function rules($user)
{
return [
'name' => 'required|min:3',
'email' => 'required|email|unique:users,email,' . $user->id,
'username' => 'required|unique:users,username,' . $user->id,
'selectedRoles' => 'required',
'password' => ['nullable', 'string', 'confirmed', Rules\Password::defaults()],
];
}
#[\Livewire\Attributes\Computed]
public function users()
{
return User::query()
->tap(fn($query) => $this->sortBy ? $query->orderBy($this->sortBy, $this->sortDirection) : $query)
->with('roles')
->paginate(15);
}
/**
* Shows the form to add a new user.
*/
public function showAddForm()
{
$this->resetFormFields();
$this->resetValidation();
Flux::modal('add-user')->show();
}
/**
* Shows the form to edit an existing user.
* @param User $user
*/
public function showEditForm(User $user)
{
$this->userId = $user->id;
$this->name = $user->name;
$this->email = $user->email;
$this->username = $user->username;
$this->selectedRoles = $user->getRoleNames();
Flux::modal('edit-user-' . $user->id)->show();
}
/**
* Resets form fields to their default values.
*/
private function resetFormFields()
{
$this->userId = '';
$this->name = '';
$this->email = '';
$this->username = '';
$this->password = '';
$this->password_confirmation = '';
$this->selectedRoles = [];
}
/**
* Saves the user (create or update based on userId).
* @param int|null $userId
*/
public function save($userId = null)
{
$userId ? $this->update(User::find($userId) ?: new User) : $this->create();
}
/**
* Creates a new user.
*/
public function create()
{
$this->validate($this->rules(new User()));
$user = User::create([
'name' => $this->name,
'email' => $this->email,
'username' => $this->username,
'password' => bcrypt($this->password ?: 'password'),
]);
$user->syncRoles($this->selectedRoles);
Flux::modals()->close();
Flux::Toast(variant: 'success', text: 'User added successfully.');
}
/**
* Updates an existing user.
* @param User $user
*/
public function update(User $user)
{
$this->validate($this->rules($user));
$user->update([
'name' => $this->name,
'email' => $this->email,
'username' => $this->username,
'password' => $this->password ? bcrypt($this->password) : $user->password,
]);
$user->syncRoles($this->selectedRoles);
Flux::modals()->close();
Flux::Toast(variant: 'success', text: 'User updated successfully.');
}
/**
* Deletes a user.
* @param User $user
*/
public function delete(User $user)
{
$user->delete();
Flux::modals()->close();
Flux::Toast(variant: 'danger', text: 'User deleted successfully.');
}
}; ?>
<section>
<div class="flex justify-between items-center">
<div>
<flux:heading>Users List</flux:heading>
<flux:subheading>A list of our current users.</flux:subheading>
</div>
<div class="flex">
<flux:spacer />
<flux:button wire:click="showAddForm">Add User</flux:button>
</div>
</div>
<flux:table :paginate="$this->users">
<flux:columns>
<flux:column sortable :sorted="$sortBy === 'id'" :direction="$sortDirection" wire:click="sort('id')" class="w-2">ID</flux:column>
<flux:column sortable :sorted="$sortBy === 'name'" :direction="$sortDirection" wire:click="sort('name')">Name</flux:column>
<flux:column sortable :sorted="$sortBy === 'email'" :direction="$sortDirection" wire:click="sort('email')">Email</flux:column>
<flux:column>Role(s)</flux:column>
<flux:column></flux:column>
</flux:columns>
<flux:rows>
@foreach ($this->users as $user)
<flux:row :key="$user->id">
<flux:cell class="whitespace-nowrap">{{ $user->id }}</flux:cell>
<flux:cell class="whitespace-nowrap">{{ $user->name }}</flux:cell>
<flux:cell class="whitespace-nowrap">{{ $user->email }}</flux:cell>
<flux:cell class="whitespace-nowrap">
@foreach($user->roles as $role)
<flux:badge>{{ $role->name }}</flux:badge>
@endforeach
</flux:cell>
<flux:cell class="text-right">
<flux:button icon="pencil-square" size="sm" inset="top bottom" wire:click="showEditForm({{ $user->id }})" />
@if(!$user->hasRole('Super Admin'))
<flux:modal.trigger name="delete-profile-{{ $user->id }}">
<flux:button variant="danger" icon="trash" size="sm" inset="top bottom" />
</flux:modal.trigger>
@endif
</flux:cell>
</flux:row>
@endforeach
</flux:rows>
</flux:table>
<flux:modal name="add-user" class="w-full space-y-6">
@include('livewire.admin.user-management.form')
</flux:modal>
@foreach ($this->users as $user)
<flux:modal name="edit-user-{{ $user->id }}" class="w-full space-y-6">
@include('livewire.admin.user-management.form')
</flux:modal>
<flux:modal name="delete-profile-{{ $user->id }}" class="min-w-[22rem] space-y-6">
<div>
<flux:heading size="lg">Delete User?</flux:heading>
<flux:subheading>
<p>You're about to delete the user: {{ $user->name }}.</p>
<p>This action cannot be reversed.</p>
</flux:subheading>
</div>
<div class="flex gap-2">
<flux:spacer />
<flux:modal.close>
<flux:button variant="ghost">Cancel</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="danger" wire:click="delete({{ $user->id }})">Delete User</flux:button>
</div>
</flux:modal>
@endforeach
</section>