javascriptphplaravellaravel-livewiresweetalert2

Sweetalert didn't fired after Laravel backend event


I'm using SweetAlert2 to show the notification for my TALL (Laravel 11 x Livewire 3) Stack application. I tried to make it re-usable by utilizing Laravel Trait and put the frontend code on the main layout and should be fired whenever the session has generated from laravel's backend

Livewire class :

<?php

namespace App\Livewire;

use App\Traits\Swalable;
use Livewire\Attributes\Title;
use Livewire\Component;

class Index extends Component
{
    use Swalable;

    public function save()
    {
        // Do something on backend

        $this->flashSuccess('The process was sucessfully done.');
    }

    #[Title('Index page')]
    public function render()
    {
        return view('livewire.index');
    }
}

Swalable Trait :

<?php

namespace App\Traits;

trait Swalable {
    public function flashSuccess($message) {
        $this->setupFlash("Sukses", $message, 'success');
    }

    public function flashError($message) {
        $this->setupFlash("Gagal", $message, 'error');
    }

    private function setupFlash($title, $message, $type) {
        session()->flash('swalMsg', [
            'title' => $title,
            'message' => $message,
            'type' => $type,
        ]);
    }
}

Main layout :

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>{{ $title ?? 'Page Title' }}</title>

        @vite(['resources/css/app.css','resources/js/app.js'])
    </head>
    <body>
        {{ $slot }}

        @if (session()->has('swalMsg'))
            <script>
                document.addEventListener("DOMContentLoaded", function(event) {
                    notification = @json(session()->pull("swalMsg"));
                    Swal.fire({
                        title: notification.title,
                        text: notification.message,
                        icon: notification.type,
                        confirmButtonText: 'Cool'
                    })

                    @php
                        session()->forget('swalMsg'); 
                    @endphp
                })
            </script>
        @endif
    </body>
</html>

The intention was after the save() method on the backend done, the sweet alert should be fired, however nothing happened,

What I've tried so far :

  1. I tried to hardcoded the parameters from backend, but the alert still didn't fired.

  2. Try to fired directly after page fully loaded (without event from the backend), it did work and fired the alert perfectly. So I think the frontend is not the problem

  3. Adding the redirect after flashing, it worked, but i didn't want the page reloaded, so this is should be the real question for my case, how to showing the alert without refreshing the page

how to achieve this ?


Solution

  • I would suggest that you just dispatch an event from the component via $this->dispatch(), which you can listen for on the window. This means no flashing, and no need to attach multiple events or anything.

    In your trait, change it so that you dispatch an event via $this->dispatch(),

    trait Swalable {
        public function flashSuccess($message) {
            $this->setupFlash("Sukses", $message, 'success');
        }
    
        public function flashError($message) {
            $this->setupFlash("Gagal", $message, 'error');
        }
    
        private function setupFlash($title, $message, $type) {
            $this->dispatch('swalMsg',
                title: $title,
                message: $message,
                type: $type,
            );
        }
    }
    

    Then in your layout, just listen for that event, with window.addEventListener("swalMsg", ...).

    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
            <title>{{ $title ?? 'Page Title' }}</title>
    
            @vite(['resources/css/app.css','resources/js/app.js'])
        </head>
        <body>
            {{ $slot }}
            
            <script>
                window.addEventListener("swalMsg", function(event) {
                    const notification = event.detail;
                    Swal.fire({
                        title: notification.title,
                        text: notification.message,
                        icon: notification.type,
                        confirmButtonText: 'Cool'
                    });
                })
            </script>
        </body>
    </html>