are you seeing network requests firing when you interact with the form? My first suspicion is that the livewirescripts is not loaded.
Livewire component lifecycle hooks not firing
I need some help determining why my modal form fields are not being validated. I've tried using both the $rules property and the rules function with no success. I've added the update/updating/updated life cycle hooks with Log statements and nothing is being written to my log. For example: if I set fields to be required and enter the first field then tab through, nothing is triggered and nothing is written to the log.
Furthermore, I'm having similar issues with unique fields such as an email field, the form won't display the error that the email address was taken.
I've been reading many articles on this today and I've tried quite a few suggested ways to fix it where others have gotten it to work.
I'm hoping someone can fill me in on what I'm missing.
Users.php
namespace App\Http\Livewire\Admin;
use App\Models\User;
use Dotenv\Exception\ValidationException;
use Exception;
use Hash;
use Livewire\Component;
use Livewire\WithPagination;
use Log;
use Spatie\Permission\Models\Role;
class Users extends Component
{
use WithPagination;
public $componentId = null;
public int $currentPage = 1;
public int $perPage = 10;
public string $success = '';
public bool $showModal = false;
public bool $showMode = false;
public $authUser;
public ?int $userId = null;
public string $name = '';
public string $phone = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
public $roles;
public $selectedRole;
protected $rules = [
'name' => 'required',
];
// public function rules(): array
// {
// return [
// 'name' => 'required|string|max:255',
// 'email' => 'required|string|max:255|email|unique:users,email,'. (string)($this->userId),
// 'phone' => 'digits:11',
// 'password' => 'min:8|required_with:password_confirmation|same:password_confirmation',
// 'password_confirmation' => 'min:8',
// 'selectedRole' => 'required',
// ];
// }
public function mount(): void
{
$this->roles = Role::all()->pluck('name', 'id')->prepend('Select Role');
$this->selectedRole = collect();
$this->authUser = Auth()->user();
}
public function updateName($value): void
{
Log::info("Update Name field with {$value} ");
}
public function updatingName($value): void
{
Log::info("Updating Name field with {$value} ");
}
public function updatedName($value): void
{
Log::info("Updated Name field with {$value} ");
}
public function updateEmail($value): void
{
Log::info("Update Email with {$value} ");
}
public function updatingEmail($value): void
{
Log::info("Updating Email with {$value} ");
}
public function updatedEmail($value): void
{
Log::info("Updated Email with {$value} ");
}
public function updated($propertyName ): void
{
Log::info("property {$propertyName} triggered");
try {
$this->validateOnly($propertyName, $this->rules);
} catch (\Illuminate\Validation\ValidationException $ex) {
$this->addError($propertyName, $ex->getMessage());
}
}
public function create(): void
{
$this->resetErrorBag();
$this->resetValidation();
$this->showModal = true;
}
public function edit($id): void
{
$this->showModal = true;
$this->userId = $id;
$user = User::findOrFail($this->userId);
$this->name = $user->name;
$this->email = $user->email;
$this->phone = $user->phone;
$this->selectedRole = $user->getRoleNames();
}
public function store(): void
{
try {
$validatedData = $this->validate();
$user = User::updateOrcreate([
'id' => $this->userId
],[
'name' => $validatedData['name'],
'email' => $validatedData['email'],
'phone' => $validatedData['phone'],
'password' => Hash::make($validatedData['password']),
]);
$user->assignRole($validatedData['selectedRole']);
$updateOrCreateVerb = ($this->userId) ? 'updated' : 'created';
$this->success = "User {$user->name} was {$updateOrCreateVerb} successfully!";
} catch (ValidationException | Exception $ex){
$this->addError('email', $ex->getMessage());
}
}
public function show($id): void
{
$this->reset();
$this->userId = $id;
$user = User::findOrFail($id);
$this->populateFields($user);
$this->showMode = true;
$this->showModal = true;
}
public function populateFields($user): void
{
$this->name = $user->name;
$this->email = $user->email;
$this->phone = $user->phone;
$this->selectedRoles = $user->getRoleNames();
}
public function delete($userId): void
{
$user = User::findOrFail($userId);
if ($user) {
$user->delete();
}
}
public function close(): void
{
$this->showModal = false;
}
public function resetSuccess(): void
{
$this->success = '';
$this->showModal = !$this->showModal;
}
public function render()
{
$this->roles = Role::all()->pluck('name', 'id')->prepend('Select Role');
return view('livewire.admin.users', [
'users' => User::latest()->orderBy('id', 'ASC')->paginate($this->perPage),
]);
}
users.blade.php
<div>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('User Management') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="inline-block mb-5">
<button wire:click="create" type="button" class="bg-indigo-500 hover:bg-indigo-600 text-white text-sm
py-2.5 px-5 rounded-md transition duration-500 ease-in-out transform hover:-translate-y-1 shadow-l">
Create New User
</button>
</div>
<div class="inline-block min-w-full shadow overflow-hidden">
<table class="min-w-full leading-normal">
<thead>
<tr>
<th class="px-3 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
{{ __('Name') }}
</th>
<th class="px-3 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
{{ __('Email') }}
</th>
<th class="px-3 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
{{ __('Phone') }}
</th>
<th class="px-3 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
{{ __('Roles') }}
</th>
<th class="px-5 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
</th>
</tr>
</thead>
<tbody>
@forelse ($users as $user)
<tr>
<td class="px-5 py-2 bg-white text-left text-sm @if (!$loop->last) border-gray-200 border-b @endif">
{{ $user->name }}
</td>
<td class="px-5 py-2 bg-white text-left text-sm @if (!$loop->last) border-gray-200 border-b @endif">
{{ $user->email }}
</td>
<td class="px-5 py-2 bg-white text-left text-sm @if (!$loop->last) border-gray-200 border-b @endif">
{{ $user->phone }}
</td>
<td class="px-5 py-2 bg-white text-left text-sm @if (!$loop->last) border-gray-200 border-b @endif">
@forelse($user->getRoleNames() as $role)
<span class="badge badge-{{ str_replace(' ', '_', Str::lower( $role )) }}">{{ $role }}</span>
@empty
{{ __('No Roles Found') }}
@endforelse
</td>
<td class="px-5 py-2 bg-white text-sm @if (!$loop->last) border-gray-200 border-b @endif text-right">
<div class="inline-block whitespace-no-wrap">
<button wire:click.prevent="edit({{ $user->id }})" type="button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold px-4 py-1 rounded">
<x-icons.editpad />
</button>
<button wire:click.prevent="$emit('triggerDelete',{{ $user }})" class="bg-red-500 hover:bg-red-700 text-white font-bold px-4 py-1 rounded">
<x-icons.trashcan />
</button>
</div>
</td>
</tr>
@empty
<tr>
<td>
{{ __('No Users Found') }}
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $users->links() }}
</div>
</div>
<div class="@if (!$showModal) hidden @endif flex items-center justify-center fixed left-0 bottom-0 w-full h-full bg-gray-800 bg-opacity-90">
{{-- Modal --}}
<div class="bg-white rounded-lg w-2/5">
@if ($errors->any())
<div class="relative m-4 px-4 py-3 text-sm bg-red-100 border border-red-400 text-red-700 rounded" role="alert">
<strong class="font-bold">Oops!</strong><span class="block sm:inline">There are some errors with your submission.</span>
</div>
@endif
@if ($success)
<div class="relative m-4 px-4 py-3 text-sm bg-green-100 border border-green-400 text-green-700 rounded" role="alert">
<span class="block sm:inline">{{ $success }}</span>
{{-- <span wire:click="resetSuccess" class="absolute top-0 bottom-0 right-0 px-4 py-3">--}}
{{-- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">--}}
{{-- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />--}}
{{-- </svg>--}}
{{-- </span>--}}
</div>
@endif
<form wire:submit.prevent="store">
@csrf
<div class="flex flex-col items-start p-4">
<div class="flex items-center w-full mb-4">
<div class="text-gray-900 font-medium text-lg">{{ $userId ? 'Edit User' : 'Create New User' }}</div>
<svg wire:click="close" class="ml-auto fill-current text-gray-700 w-6 h-6 cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
<path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/>
</svg>
</div>
<div class="w-full">
<label class="block font-semibold text-sm text-gray-700" for="name">Name</label>
<input wire:model.defer="name" id="name" name="name" type="text"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 focus:outline-none focus:border-blue-400"
autocomplete="name"/>
@error('name') <span class="text-xs text-red-500 mt-1">{{ $message }}</span>@enderror
</div>
<div class="w-full">
<div class="flex flex-wrap -mx-3 sm:-mx-4 md:-mx-4 lg:-mx-4 xl:-mx-4 overflow-hidden">
<div class="my-2 px-3 w-1/2 sm:my-2 sm:px-4 sm:w-1/2 md:my-2 md:px-4 md:w-1/2 lg:my-2 lg:px-3 lg:w-1/2 xl:my-2 xl:px-4 xl:w-1/2 overflow-hidden">
<label class="block font-semibold text-sm text-gray-700" for="email">Email</label>
<input wire:model.defer="email" id="email" name="email" type="email"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 focus:outline-none focus:border-blue-400"
autocomplete="email"/>
@error('email') <span class="text-xs text-red-500 mt-1">{{ $message }}</span> @enderror
</div>
<div class="my-2 px-3 w-1/2 sm:my-2 sm:px-2 sm:w-1/2 md:my-2 md:px-4 md:w-1/2 lg:my-2 lg:px-3 lg:w-1/2 xl:my-2 xl:px-4 xl:w-1/2 overflow-hidden">
<label class="block font-semibold text-sm text-gray-700" for="phone">Phone</label>
<input wire:model.defer="phone" id="phone" name="guard_name" type="tel"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 focus:outline-none focus:border-blue-400"/>
@error('phone') <span class="text-xs text-red-500 mt-1">{{ $message }}</span> @enderror
</div>
</div>
</div>
<div class="w-full">
<div class="flex flex-wrap -mx-3 sm:-mx-4 md:-mx-4 lg:-mx-4 xl:-mx-4 overflow-hidden">
<div class="my-2 px-3 w-1/2 sm:my-2 sm:px-4 sm:w-1/2 md:my-2 md:px-4 md:w-1/2 lg:my-2 lg:px-3 lg:w-1/2 xl:my-2 xl:px-4 xl:w-1/2 overflow-hidden">
<label class="block font-semibold text-sm text-gray-700" for="password">Password</label>
<input wire:model.defer="password" id="password" name="password" type="password"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 focus:outline-none focus:border-blue-400"/>
@error('password') <span class="text-xs text-red-500 mt-1">{{ $message }}</span> @enderror
</div>
<div class="my-2 px-3 w-1/2 sm:my-2 sm:px-4 sm:w-1/2 md:my-2 md:px-4 md:w-1/2 lg:my-2 lg:px-3 lg:w-1/2 xl:my-2 xl:px-4 xl:w-1/2 overflow-hidden">
<label class="block font-semibold text-sm text-gray-700" for="password_confirm">Password Confirmation</label>
<input wire:model.defer="password_confirmation" id="password_confirm" name="password_confirm" type="password" autocomplete="new-password"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 focus:outline-none focus:border-blue-400"/>
@error('password_confirm') <span class="text-xs text-red-500 mt-1">{{ $message }}</span> @enderror
</div>
</div>
</div>
<div class="w-full">
<label class="block mb-3 font-semibold text-sm text-gray-700" for="selectedRole">Roles</label>
<select wire:model.defer="selectedRole" id="selectedRole" name="selectedRole" type="text"
class="text-sm sm:text-base rounded-lg border border-gray-400 w-full pl-2 pr-4 py-2 mb-2 focus:outline-none focus:border-blue-400"
autocomplete="selectedRole">
@foreach($roles as $id => $role)
<option value="{{ $role }}" @if ($role === $selectedRole)) selected @endif>{{ $role }}</option>
@endforeach
</select>
@error('selectedRole') <span class="text-xs text-red-500 mt-1">{{ $message }}</span>@enderror
</div>
<div class="ml-auto mt-5">
<button wire:click="close" type="button" class="bg-gray-500 text-white font-bold py-2 px-4 rounded" data-dismiss="modal">
Close
</button>
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
{{ $userId ? 'Save Changes' : 'Save' }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
@this.on('triggerDelete', userId => {
Swal.fire({
title: 'Are You Sure?',
text: 'User record will be deleted!',
type: "warning",
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Delete!'
}).then((result) => {
if (result.value) {
@this.call('delete',userId)
} else {
console.log("Canceled");
}
});
});
})
</script>
@endpush
Please or to participate in this conversation.