JavaScript & Events

Gale provides two powerful methods for JavaScript integration: dispatch() for firing custom DOM events, and js() for executing arbitrary JavaScript. Both enable seamless communication between your Laravel backend and frontend.

Dispatching Events

Use dispatch() to fire custom DOM events that your Alpine.js components can listen to. Events are dispatched on the window by default.

// Window-level event with data
gale()->dispatch('user-updated', [
    'id' => $user->id,
    'name' => $user->name,
]);

// Simple event without data
gale()->dispatch('cart-updated');

// Event with notification data
gale()->dispatch('show-notification', [
    'type' => 'success',
    'message' => 'Changes saved successfully!',
]);

Listening to Events

Listen for dispatched events using Alpine.js event listeners with the .window modifier. Event data is available via $event.detail.

<!-- Listen for user-updated event -->
<div
    x-data="{ user: null }"
    @user-updated.window="user = $event.detail">

    <template x-if="user">
        <p>Updated: <span x-text="user.name"></span></p>
    </template>
</div>

<!-- Listen for notification events -->
<div
    x-data="{ notifications: [] }"
    @show-notification.window="
        notifications.push($event.detail);
        setTimeout(() => notifications.shift(), 3000);
    ">

    <template x-for="notif in notifications">
        <div :class="'alert alert-' + notif.type" x-text="notif.message"></div>
    </template>
</div>

Targeted Events

Dispatch events to specific elements using the selector option:

// Target by class
gale()->dispatch('refresh', ['section' => 'cart'], [
    'selector' => '.shopping-cart',
]);

// Target by ID
gale()->dispatch('update', ['data' => $data], [
    'selector' => '#user-profile',
]);

// Target multiple elements
gale()->dispatch('price-changed', ['price' => $newPrice], [
    'selector' => '[data-product]',
]);

Listen without the .window modifier for targeted events:

<!-- Element receives targeted event directly -->
<div
    class="shopping-cart"
    x-data="{ items: [] }"
    @refresh="$get('/cart')">
    <!-- Cart content -->
</div>

Event Options

Customize event behavior with standard DOM event options:

gale()->dispatch('notification', ['message' => 'Saved!'], [
    'bubbles' => true,      // Event bubbles up the DOM (default: true)
    'cancelable' => true,  // Event can be canceled (default: true)
    'composed' => false,   // Event crosses shadow DOM (default: false)
]);
Option Type Default Description
selector string null Target element(s) selector
bubbles bool true Event bubbles up DOM tree
cancelable bool true Event can be canceled
composed bool false Event crosses shadow DOM boundaries

Executing JavaScript

Use js() to execute arbitrary JavaScript in the browser:

// Simple console log
gale()->js('console.log("Hello from server")');

// Call application functions
gale()->js('myApp.showNotification("Saved!")');

// Scroll to element
gale()->js('document.getElementById("results").scrollIntoView()');

// Focus input
gale()->js('document.querySelector("#email").focus()');

// Multiple statements
gale()->js('
    const el = document.getElementById("modal");
    el.classList.add("open");
    el.querySelector("input").focus();
');

Auto-Remove Script

By default, the injected script element persists. Use autoRemove to clean up:

// Script element removed after execution
gale()->js('myApp.showNotification("Saved!")', [
    'autoRemove' => true,
]);

JavaScript with PHP Data

Safely inject PHP values into JavaScript using json_encode():

$userData = json_encode([
    'id' => $user->id,
    'name' => $user->name,
    'email' => $user->email,
]);

gale()->js("myApp.setCurrentUser({$userData})");

// Or with template
$message = json_encode($notification->message);
gale()->js("alert({$message})");

dispatch() vs js()

Feature dispatch() js()
Purpose Fire DOM events Execute arbitrary code
Integration Works with Alpine listeners Direct JavaScript execution
Data passing Via event.detail Via JSON in code string
Targeting Built-in selector option Manual querySelector
Best for Component communication Side effects, 3rd party libs

Practical Examples

Toast Notification System

// Backend controller
public function save(Request $request)
{
    $item->update($request->validated());

    return gale()
        ->dispatch('toast', [
            'type' => 'success',
            'title' => 'Saved!',
            'message' => 'Your changes have been saved.',
        ]);
}
<!-- Toast container component -->
<div
    x-data="{
        toasts: [],
        add(toast) {
            const id = Date.now();
            this.toasts.push({ ...toast, id });
            setTimeout(() => this.remove(id), 5000);
        },
        remove(id) {
            this.toasts = this.toasts.filter(t => t.id !== id);
        }
    }"
    @toast.window="add($event.detail)"
    class="fixed top-4 right-4 space-y-2 z-50">

    <template x-for="toast in toasts" :key="toast.id">
        <div
            class="p-4 rounded shadow-lg min-w-64"
            :class="{
                'bg-green-500 text-white': toast.type === 'success',
                'bg-red-500 text-white': toast.type === 'error',
                'bg-blue-500 text-white': toast.type === 'info'
            }">
            <div class="font-bold" x-text="toast.title"></div>
            <div x-text="toast.message"></div>
        </div>
    </template>
</div>

Third-Party Library Integration

// Trigger SweetAlert
gale()->js('
    Swal.fire({
        title: "Success!",
        text: "Your file has been deleted.",
        icon: "success"
    });
');

// Update Chart.js
$data = json_encode($chartData);
gale()->js("
    window.salesChart.data.datasets[0].data = {$data};
    window.salesChart.update();
");

// Trigger Confetti
gale()->js('confetti({ particleCount: 100, spread: 70 })');

Modal Control

// Open modal after save
public function save()
{
    $item = Item::create($data);

    return gale()
        ->state('item', $item)
        ->dispatch('open-modal', [
            'modal' => 'share-modal',
            'item' => $item->toArray(),
        ]);
}

// Close modal on success
public function share()
{
    // Share logic...

    return gale()
        ->dispatch('close-modal')
        ->dispatch('toast', [
            'type' => 'success',
            'message' => 'Shared successfully!',
        ]);
}

On this page