Polling & Confirmation

Gale provides x-poll for automatic data refreshing and x-confirm for action confirmation dialogs. Both directives work seamlessly with Gale's SSE-based response system.

The x-poll Directive

The x-poll directive automatically fetches data from an endpoint at regular intervals. It's perfect for real-time dashboards, status updates, and live feeds.

<!-- Poll every 5 seconds (default) -->
<div x-data="{ status: 'unknown' }" x-poll="/api/status">
    Status: <span x-text="status"></span>
</div>

Custom Interval

Set a custom polling interval using modifiers. Supports seconds (.5s) or milliseconds (.500ms).

<!-- Poll every 2 seconds -->
<div x-poll.2s="/api/notifications">...</div>

<!-- Poll every 10 seconds -->
<div x-poll.10s="/api/metrics">...</div>

<!-- Poll every 500ms for fast updates -->
<div x-poll.500ms="/api/realtime">...</div>

<!-- Plain number is treated as seconds -->
<div x-poll.3="/api/data">...</div>

Visibility-Aware Polling

Use the .visible modifier to pause polling when the browser tab is hidden. This saves bandwidth and server resources.

<!-- Only poll when tab is visible -->
<div
    x-data="{ messages: [] }"
    x-poll.visible.5s="/api/messages">

    <template x-for="msg in messages">
        <div x-text="msg.content"></div>
    </template>
</div>

Conditional Stop

Use x-poll-stop to stop polling when a condition is met. This is useful for job status monitoring or one-time data fetching.

<!-- Stop polling when job is complete -->
<div
    x-data="{ status: 'pending', progress: 0 }"
    x-poll.2s="/api/job/123"
    x-poll-stop="status === 'complete'">

    <div class="progress-bar">
        <div :style="'width: ' + progress + '%'"></div>
    </div>
    <span x-text="status"></span>
</div>

CSRF Protection

Add the .csrf modifier to include CSRF tokens with poll requests. Required for endpoints that modify data.

<!-- Include CSRF token with requests -->
<div
    x-data="{ heartbeat: null }"
    x-poll.csrf.30s="/api/heartbeat">

    Last ping: <span x-text="heartbeat"></span>
</div>

Backend Response

Poll requests work like any Gale request. Return state updates from your controller:

public function jobStatus(Job $job)
{
    return gale()->state([
        'status' => $job->status,
        'progress' => $job->progress,
        'message' => $job->status_message,
    ]);
}

public function notifications()
{
    $notifications = auth()->user()
        ->unreadNotifications()
        ->latest()
        ->take(10)
        ->get();

    return gale()->state('notifications', $notifications);
}

The x-confirm Directive

The x-confirm directive shows a confirmation dialog before allowing click or submit events to proceed. Uses the native browser dialog by default.

<!-- Basic confirmation -->
<button
    x-confirm="Are you sure you want to delete this?"
    @click="$deletex('/item/123')">
    Delete Item
</button>

Default Message

If no message is provided, the default "Are you sure?" message is shown:

<!-- Uses default "Are you sure?" message -->
<button x-confirm @click="$deletex('/reset')">
    Reset Everything
</button>

Dynamic Messages

Use expressions for dynamic confirmation messages that include data from your component:

<div x-data="{ itemName: 'Project Alpha' }">
    <!-- String concatenation -->
    <button
        x-confirm="'Delete ' + itemName + '?'"
        @click="$deletex('/item')">
        Delete
    </button>

    <!-- Template literal -->
    <button
        x-confirm=`Are you sure you want to archive ${itemName}?`
        @click="$postx('/archive')">
        Archive
    </button>
</div>

Form Submission

The x-confirm directive works on forms to confirm before submission:

<form
    x-data="{ email: '', name: '' }"
    x-confirm="Submit this registration form?"
    @submit.prevent="$postx('/register')">

    <input x-model="name" placeholder="Name">
    <input x-model="email" placeholder="Email">

    <button type="submit">Register</button>
</form>

Custom Confirm Handler

Replace the native browser dialog with a custom confirmation UI using Alpine.gale.configureConfirm():

// Configure custom confirm handler
Alpine.gale.configureConfirm((message) => {
    // Return a promise that resolves to boolean
    return new Promise((resolve) => {
        // Show your custom modal
        customModal.show({
            message,
            onConfirm: () => resolve(true),
            onCancel: () => resolve(false)
        });
    });
});

// Or use a third-party library like SweetAlert
Alpine.gale.configureConfirm((message) => {
    return Swal.fire({
        title: 'Confirm',
        text: message,
        icon: 'warning',
        showCancelButton: true,
        confirmButtonText: 'Yes',
        cancelButtonText: 'Cancel'
    }).then((result) => result.isConfirmed);
});

Live Dashboard Example

<div
    x-data="{
        metrics: {
            activeUsers: 0,
            requests: 0,
            errors: 0,
            cpu: 0,
            memory: 0
        },
        lastUpdated: null
    }"
    x-poll.visible.3s="/api/dashboard/metrics"
    class="grid grid-cols-3 gap-4">

    <!-- Metrics cards -->
    <div class="bg-white p-4 rounded shadow">
        <h3>Active Users</h3>
        <p class="text-3xl font-bold" x-text="metrics.activeUsers"></p>
    </div>

    <div class="bg-white p-4 rounded shadow">
        <h3>Requests/min</h3>
        <p class="text-3xl font-bold" x-text="metrics.requests"></p>
    </div>

    <div class="bg-white p-4 rounded shadow">
        <h3>Error Rate</h3>
        <p class="text-3xl font-bold"
           :class="metrics.errors > 5 && 'text-red-500'"
           x-text="metrics.errors + '%'"></p>
    </div>

    <!-- CPU and Memory bars -->
    <div class="col-span-3 bg-white p-4 rounded shadow">
        <div class="mb-4">
            <div class="flex justify-between mb-1">
                <span>CPU</span>
                <span x-text="metrics.cpu + '%'"></span>
            </div>
            <div class="h-2 bg-gray-200 rounded">
                <div
                    class="h-full bg-blue-500 rounded transition-all"
                    :style="'width: ' + metrics.cpu + '%'"></div>
            </div>
        </div>
        <div>
            <div class="flex justify-between mb-1">
                <span>Memory</span>
                <span x-text="metrics.memory + '%'"></span>
            </div>
            <div class="h-2 bg-gray-200 rounded">
                <div
                    class="h-full bg-green-500 rounded transition-all"
                    :style="'width: ' + metrics.memory + '%'"></div>
            </div>
        </div>
    </div>

    <!-- Last updated -->
    <div class="col-span-3 text-sm text-gray-500 text-right">
        <span x-loading>Updating...</span>
        <span x-loading.remove>Last updated: <span x-text="lastUpdated"></span></span>
    </div>
</div>

Delete Confirmation Example

<div x-data="{ items: [] }">
    <table class="w-full">
        <thead>
            <tr>
                <th>Name</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <template x-for="item in items" :key="item.id">
                <tr>
                    <td x-text="item.name"></td>
                    <td class="space-x-2">
                        <!-- Edit - no confirmation -->
                        <button
                            @click=`$get('/items/${item.id}/edit')`
                            class="text-blue-500">
                            Edit
                        </button>

                        <!-- Delete - with confirmation -->
                        <button
                            x-confirm=`Delete "${item.name}"? This cannot be undone.`
                            @click=`$deletex('/items/${item.id}')`
                            x-loading.attr="disabled"
                            class="text-red-500">
                            <span x-loading.remove>Delete</span>
                            <span x-loading>Deleting...</span>
                        </button>
                    </td>
                </tr>
            </template>
        </tbody>
    </table>
</div>

On this page