theUnforgiven's avatar

Livewire/Alpine Modal how to close upon successfully posting..?

Hi all,

I have a Livewire component:

class AddTask extends Component
{
    public $name;
    public $due_date;
    public $assigned_to;
    public $calendar;
    public $description;

    public function addTask()
    {
        $this->validate([
            'name'          => 'required|min:3',
            // 'due_date'      => 'required',
            'assigned_to'   => 'required',
            'calendar'      => 'required'
        ]);

        Tasks::create([
            'user_id' => user()->id,
            'name' => $this->name,         
            'due_date' => $this->due_date,       
            'assigned_to' => 1,  
            'calendar' => $this->calendar,
            'description' => $this->description      
        ]);
     
    
        session()->flash('success', 'Setup now complete.');
        return back();
    }
    public function render()
    {
        return view('livewire.tasks.add-task');
    }
}

I then have this in a blade file which calls a Laravel component

<div>
    <x-modal title="Add task">
        <x-slot name="trigger">
            <button class="text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add New Task</button>
        </x-slot>
        <form>
            <div class="flex w-full sm:content-center flex-wrap mb-6">
                <label for="name" class="block text-gray-700 text-sm font-bold mb-2">
                    {{ __('Name') }}
                </label>

                <input wire:model="name" id="name" type="text" class="form-input w-full @error('name') border-red-500 @enderror" value="{{ old('name') }}">

                @error('name')
                    <p class="text-red-500 text-xs italic mt-4">
                        {{ $message }}
                    </p>
                @enderror
            </div>
            <x-slot name="submit">
                <button type="submit"
                        wire:click="addTask"
                        class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-teal-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-teal-500 focus:outline-none focus:border-teal-700 focus:shadow-outline-teal transition ease-in-out duration-150 sm:text-sm sm:leading-5"
                >
                Add Task
                </button>
            </x-slot>
        </form>
    </x-modal>
</div>

x-model component, just standard stuff with Tailwind and AlpineJS

<div x-data="{ on: false }">
    <span @click="on = true">{{ $trigger }}</span>

    <div class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-show="on">
      <div
          class="fixed inset-0 transition-opacity"
          x-show="on"
          x-transition:enter="ease-out duration-300"
          x-transition:enter-start="opacity-0"
          x-transition:enter-end="opacity-100"
          x-transition:leave="ease-in duration-200"
          x-transition:leave-start="opacity-100"
          x-transition:leave-end="opacity-0"
      >
        <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
      </div>

      <div class="bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:max-w-lg sm:w-full"
           role="dialog"
           aria-modal="true"
           aria-labelledby="modal-headline"
           x-show="on"
           x-transition:enter="ease-out duration-300"
           x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
           x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
           x-transition:leave="ease-in duration-200"
           x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
           x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
      >
        <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
          <div class="sm:flex sm:items-start">
            
            <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
              <h3 class="text-lg leading-6 font-medium text-gray-900"
                  id="modal-headline"
              >
                {{ $title ?? '' }}
              </h3>
              <div class="mt-2">
                <p class="text-sm leading-5 text-gray-500">
                  {{ $slot }}
                </p>
              </div>
            </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">
            {{ $submit }}
          </span>
          <span class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
            <button type="button"
                    @click="on = false"
                    class="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-base leading-6 font-medium text-gray-700 shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5"
            >
              {{ $cancelLabel ?? 'Cancel' }}
            </button>
          </span>
        </div>
    </div>
</div>

</div>

My question is within the Livewire component just before this takes place

session()->flash('success', 'Setup now complete.');
return back();

How would I fire off the event to close the modal window and show the flash message? What do I need javascript wise and what should be called in the Livewire component?

0 likes
14 replies
Snapey's avatar

Change the submit to call a function in the livewire component.

After saving the data, you will re-render the component, and the x-data with on: false will close the modal ?

Your flash message can work like non-livewire if you put the alert code within the modal, else, you need to emit an event that can be detected by alpine elsewhere on the page.

theUnforgiven's avatar

The submit does call the Livewire component as the data saves, I just can't figure out how to close the modal at that point.

Snapey's avatar

I've looked over your code multiple times, and cannot fathom what you are trying to do... sorry. Me being confused by livewire component and Laravel 7 component and modal and wire-model and x-model component which I assume you mean x-modal component ?

When your component renders, why would the modal not be closed, thanks to x-show?

theUnforgiven's avatar

It's ok buddy, I was confused at first. I just can't figure out how to close the modal.

The 2nd block of code I put starting <div> <x-modal title="Add task">... is the Livewire blade file.

The last block of code is the <x-modal> laravel component.

theUnforgiven's avatar

When the component renders the modal isn't shown, I use the click event handler to initialise it and show it, but upon filling the form out I want to close it. I understand with Bootstrap this is staright forward, but i'm using Tailwind, Alpine and Livewire, well trying to ;)

Snapey's avatar

so if the x-data is in the wrapper around the livewire component then you will need to emit an event from livewire so that alpine knows to close the modal.

You have to add a global livewire listener like https://laravel-livewire.com/docs/events#in-js and then emit an event from that listener that alpine can hear. (I think)

theUnforgiven's avatar

Yeah, that's what I thought, but I tried what's in the docs and it doesn't show as fired.

Snapey's avatar

you tried that code in your view, with a simple console.log?

Then in the livewire class, used $this->emit() to send the event?

2 likes
theUnforgiven's avatar
theUnforgiven
OP
Best Answer
Level 51

After a quick convo on twitter , I found I needed to do the following:

$this->dispatchBrowserEvent('close-modal');

then on the x-data do :

x-on:close-modal.window="on = false"
11 likes
Snapey's avatar

hmm, undocumented super power.

2 likes
cmackinlay's avatar

Had the same problem and had figured the same solution but couldn't make it work. Discovered that either 'dispatchBrowserEvent parameter' or 'x-on' is case sensitive!

$this->dispatchBrowserEvent('closeModal'); combined with x-on:closeModal.window="on = false" doesn't work but:

$this->dispatchBrowserEvent('closemodal'); combined with x-on:closemodal.window="on = false" does!!

1 like
ouldnajem's avatar

This work for me:

<span x-on:click="on = false">
	<button wire:click="update()"  class="...">
		Update
	 </button>
</span>
awaix's avatar

@theunforgiven

Im using L8 with livewire default install.. Im trying to put my login and register forms in a modal window.. i have no clue on how to display the modal at all.. i tried copying your example above but no luck... would it be possible if you could write a step by step on how you managed to display modal window on button click with a form inside?

i did php artisan make:liverwire loginModal which created 2 files, resources>views>livewire>loginmodal.blade.php and Http>Livewire>Loginmodal.php

so i added class code (livewire component) from above into http>livewire>loginmodal.php and then added modal window code into loginmodal.blade.php and 3rd bit into welcome.blade.php

now im getting undefined variable $trigger. sorry if i sound lame.. but i cant figure this out for some reason. i hope you can help. Thanks.

Please or to participate in this conversation.