EN
Getting started
75 · Forms

Form guard

A form with unsaved-changes protection. With dirty-check on, leaving the page after fields have changed asks for confirmation: the browser's own warning when leaving through the browser, and the acun:form-guard-dialog dialog on wire:navigate transitions.

PRO acun:form · acun:form-guard-dialog
01

Basic usage

acun:form renders a normal <form>; dirty-check turns the protection on. The guard compares the current values of the fields with their values when the page was opened: if you change a field and set it back to its old value, the form counts as "clean" again. The status line below shows the result of this comparison (AcunUI.pro.formGuard.dirty). The old name window.formDirty gives the same value.

LIVE EXAMPLE
<acun:form dirty-check class="space-y-4"
    x-data="{ changed: false }"
    x-on:input="setTimeout(() => changed = AcunUI.pro.formGuard.dirty)"
    x-on:change="setTimeout(() => changed = AcunUI.pro.formGuard.dirty)">
    <acun:input name="customer_name" label="Customer name" value="Ayşe Yılmaz" />
    <acun:checkbox name="active" label="Active customer" checked />
    <p class="text-sm text-gray-500 dark:text-zinc-400" x-text="changed ? 'There are unsaved changes' : 'No changes'"></p>
</acun:form>
PropValues
dirty-checktrue | false
02

Livewire form

In Livewire forms, attributes such as wire:submit go straight to the form. Guarded forms added later by a Livewire update are also tracked automatically.

BLADE
<acun:form wire:submit="save" dirty-check class="space-y-4">
    <acun:input wire:model="customer_name" label="Customer name" />
    <acun:select wire:model="district_id" label="District" :options="$districts" />
    <acun:button type="submit">Save</acun:button>
</acun:form>
03

After saving

When saving succeeds, or when another record is loaded into the form, dispatch the form-saved event; the forms' current values are taken as the new starting point. When acun:modal closes, only the forms inside it are reset automatically.

PHP
public function save(): void
{
    $this->validate();

    $this->customer->update($this->only(['customer_name', 'district_id']));

    $this->dispatch('form-saved');
}
PropValues
form-savedEvent; marks all guarded forms as clean
acun:modal:closedEvent; dispatched when acun:modal closes (the old name modal-closed is also dispatched), resets the forms inside it
04

Action bar

When placed in a form, acun:form-actions (PRO) binds itself to the form it is in; no extra attribute is needed. Save is enabled only when a field changes, and Previous/Next/Add/Delete only on a saved, clean record. Cancel restores the form to its last clean values (including wire:model values, select boxes and tag inputs); if cancel="revert" is given, that Livewire method is called instead and the values it returns count as the new clean state. sticky keeps the bar at the bottom of the screen on long forms; since it takes up space at the end of the form, nothing on the page shifts when it sticks. If you give the form an id, the InteractsWithFormModal events (formRecordLoaded('product-form'), formSaved('product-form')) update the bar's state and also refresh the guard's starting values.

BLADE
<acun:form id="product-form" wire:submit="save" dirty-check class="space-y-6">
    <acun:input wire:model="name" label="Product name" required />
    <acun:combobox wire:model="tags" label="Tags" multiple :options="$tagOptions" />

    <acun:form-actions sticky cancel="revert">
        <p class="text-xs text-gray-500 dark:text-zinc-400">{{ $name }}</p>
    </acun:form-actions>
</acun:form>
PropValues
stickyBar that stays at the bottom of the screen
canceltrue: last clean values · "revert": Livewire method
slotStatus line at the start of the bar
05

What counts

The fields in the form are included in the comparison; fields carrying data-dirty-ignore (or those inside a container carrying this attribute) are not counted. Components that are not real form fields (acun:combobox, acun:tag-input, acun:rating, acun:input-otp) keep their value in hidden fields named after the property when bound with wire:model, and fire a change event when the user changes it; search and typing boxes and code boxes are not counted.

BLADE
<acun:form wire:submit="save" dirty-check>
    <acun:tag-input wire:model="keywords" label="Keywords" />
    <input type="search" placeholder="Search the list" data-dirty-ignore>
</acun:form>
PropValues
data-dirty-ignoreField or container; its changes are not counted
06

Confirmation dialog

If there are changes and the user tries to go to another page with wire:navigate, the transition is stopped and acun:form-guard-dialog opens; "Continue" completes the transition. acun:layout.sidebar and acun:layout.horizontal already include this dialog; in your own layout, add it once. When leaving through the browser, such as reloading or closing the tab, the browser's own warning appears. In this example the dialog is opened with a button.

LIVE EXAMPLE
<acun:button variant="white" x-data x-on:click="$dispatch('form-guard-confirm', { url: null })">Show the dialog</acun:button>

<acun:form-guard-dialog />
API

Props and slots

The values the component accepts.

PropDefaultDescription
form: dirty-checkfalseTurns on unsaved-changes protection (data-dirty-check)
form-actions: stickyfalsePRO: the bar stays at the bottom of the screen (without shifting the layout)
form-actions: canceltruePRO: Cancel returns to the last clean values; when a method name is given, that Livewire method is called
form-guard-dialog — Takes no props; added to the layout once (already included in the ready-made layouts)
Slots: form: default (form fields)
Acun UI · Form guardDetailed usage and examples