# Livewire Modals The Admin package uses Livewire components as full-page modals, providing a seamless admin interface without traditional page reloads. ## Overview Livewire modals in Core PHP: - Render as full-page routes - Support direct URL access - Maintain browser history - Work with back/forward buttons - No JavaScript modal libraries needed ## Creating a Modal ### Basic Modal ```php 'required|max:255', 'content' => 'required', 'status' => 'required|in:draft,published', ]; public function mount(?Post $post = null): void { $this->post = $post; if ($post) { $this->title = $post->title; $this->content = $post->content; $this->status = $post->status; } } public function save(): void { $validated = $this->validate(); if ($this->post) { $this->post->update($validated); $message = 'Post updated successfully'; } else { Post::create($validated); $message = 'Post created successfully'; } session()->flash('success', $message); $this->redirect(route('admin.blog.posts')); } public function render() { return view('blog::admin.post-editor') ->layout('admin::layouts.modal'); } } ``` ### Modal View ```blade {{-- resources/views/admin/post-editor.blade.php --}}

{{ $post ? 'Edit Post' : 'Create Post' }}

{{ $post ? 'Update' : 'Create' }} Post Cancel

Publishing Tips

  • Write a clear, descriptive title
  • Use proper formatting in content
  • Save as draft to preview first
``` ## Registering Modal Routes ```php // Routes/admin.php use Mod\Blog\View\Modal\Admin\PostEditor; use Mod\Blog\View\Modal\Admin\PostsList; Route::middleware(['web', 'auth', 'admin'])->prefix('admin/blog')->group(function () { Route::get('/posts', PostsList::class)->name('admin.blog.posts'); Route::get('/posts/create', PostEditor::class)->name('admin.blog.posts.create'); Route::get('/posts/{post}/edit', PostEditor::class)->name('admin.blog.posts.edit'); }); ``` ## Opening Modals ### Via Link ```blade New Post ``` ### Via Livewire Navigate ```blade ``` ### Via JavaScript ```blade ``` ## Modal Layouts ### With HLCRF ```blade Modal Header Modal Content Modal Footer ``` ### Full-Width Modal ```blade Full-width content ``` ### With Sidebar ```blade Main content Sidebar ``` ## Advanced Patterns ### Modal with Confirmation ```php public bool $showDeleteConfirmation = false; public function confirmDelete(): void { $this->showDeleteConfirmation = true; } public function delete(): void { $this->post->delete(); session()->flash('success', 'Post deleted'); $this->redirect(route('admin.blog.posts')); } public function cancelDelete(): void { $this->showDeleteConfirmation = false; } ``` ```blade @if($showDeleteConfirmation)

Delete Post?

This action cannot be undone.

Delete Cancel
@endif ``` ### Modal with Steps ```php public int $step = 1; public function nextStep(): void { $this->validateOnly('step' . $this->step); $this->step++; } public function previousStep(): void { $this->step--; } ``` ```blade
@if($step === 1) {{-- Step 1: Basic Info --}} Next @elseif($step === 2) {{-- Step 2: Content --}} Back Next @else {{-- Step 3: Review --}}
Review and save...
Back Save @endif
``` ### Modal with Live Search ```php public string $search = ''; public array $results = []; public function updatedSearch(): void { $this->results = Post::where('title', 'like', "%{$this->search}%") ->limit(10) ->get() ->toArray(); } ``` ```blade
@foreach($results as $result)
{{ $result['title'] }}
@endforeach
``` ## File Uploads ### Single File ```php use Livewire\WithFileUploads; class PostEditor extends Component { use WithFileUploads; public $image; public function save(): void { $this->validate([ 'image' => 'required|image|max:2048', ]); $path = $this->image->store('posts', 'public'); Post::create([ 'image_path' => $path, ]); } } ``` ```blade @if($image) @endif ``` ### Multiple Files ```php public array $images = []; public function save(): void { $this->validate([ 'images.*' => 'image|max:2048', ]); foreach ($this->images as $image) { $path = $image->store('posts', 'public'); // Save path... } } ``` ## Real-Time Validation ```php protected array $rules = [ 'title' => 'required|max:255', 'slug' => 'required|unique:posts,slug', ]; public function updated($propertyName): void { $this->validateOnly($propertyName); } ``` ```blade @error('slug')

{{ $message }}

@enderror ``` ## Loading States ```blade {{-- Show loading on specific action --}} Save Saving... {{-- Disable form during loading --}}
{{-- Form fields --}}
{{-- Spinner --}}
``` ## Events ### Dispatch Events ```php // From modal public function save(): void { // Save logic... $this->dispatch('post-saved', postId: $post->id); } ``` ### Listen to Events ```php // In another component protected $listeners = ['post-saved' => 'refreshPosts']; public function refreshPosts(int $postId): void { $this->posts = Post::all(); } ``` ```blade {{-- In Blade --}}
``` ## Best Practices ### 1. Use Route Model Binding ```php // ✅ Good - automatic model resolution Route::get('/posts/{post}/edit', PostEditor::class); public function mount(?Post $post = null): void { $this->post = $post; } ``` ### 2. Flash Messages ```php // ✅ Good - inform user of success public function save(): void { // Save logic... session()->flash('success', 'Post saved'); $this->redirect(route('admin.blog.posts')); } ``` ### 3. Validate Early ```php // ✅ Good - real-time validation public function updated($propertyName): void { $this->validateOnly($propertyName); } ``` ### 4. Use Loading States ```blade {{-- ✅ Good - show loading feedback --}} Save ``` ## Testing ```php set('title', 'Test Post') ->set('content', 'Test content') ->set('status', 'published') ->call('save') ->assertRedirect(route('admin.blog.posts')); $this->assertDatabaseHas('posts', [ 'title' => 'Test Post', ]); } public function test_validates_required_fields(): void { Livewire::test(PostEditor::class) ->set('title', '') ->call('save') ->assertHasErrors(['title' => 'required']); } public function test_updates_existing_post(): void { $post = Post::factory()->create(); Livewire::test(PostEditor::class, ['post' => $post]) ->set('title', 'Updated Title') ->call('save') ->assertRedirect(); $this->assertEquals('Updated Title', $post->fresh()->title); } } ``` ## Learn More - [Form Components →](/packages/admin/forms) - [HLCRF Layouts →](/packages/admin/hlcrf) - [Livewire Documentation →](https://livewire.laravel.com)