Form penceresi
Bu sayfa, kayıt ekleme ve düzenleme formunu bir pencerede (modal) açmayı, kaydetmeyi ve "kaydedilmemiş değişiklikler" uyarısını anlatır.
Not
Form penceresi PRO paketindedir; Acun temalarında kurulu gelir. Mevcut bir projede Mevcut projeye ekleme sayfasındaki adımları izleyin: stiller
acun-ui-pro.cssiçindedir, davranışıimport '@acunsoft/acun-ui-pro'kurar.
En kısa örnek
app/Livewire/Customers/CustomerForm.php:
namespace App\Livewire\Customers;
use Acun\Ui\Form\Concerns\InteractsWithFormModal;
use App\Models\Customer;
use Illuminate\Validation\ValidationException;
use Livewire\Component;
final class CustomerForm extends Component
{
use InteractsWithFormModal;
public bool $showForm = false;
public string $name = '';
// "Yeni müşteri" düğmesi ve çubuktaki "Ekle" düğmesi bunu çağırır.
public function createNew(): void
{
$this->reset('name');
$this->formCreating('customer-form');
$this->showForm = true;
}
public function save(): void
{
$this->formSaving('customer-form');
try {
Customer::create($this->validate(['name' => ['required', 'string', 'max:120']]));
} catch (ValidationException $exception) {
$this->formSaveFailed('customer-form');
throw $exception;
}
$this->formSaved('customer-form');
$this->showForm = false;
}
public function render()
{
return view('livewire.customers.customer-form');
}
}
resources/views/livewire/customers/customer-form.blade.php:
<div>
<acun:button wire:click="createNew">Yeni müşteri</acun:button>
<acun:form-modal
id="customer-form"
title="Müşteri"
wire:model="showForm"
wire:submit="save"
:mode="$formMode"
:record-exists="$formRecordExists"
:has-previous="$formHasPrevious"
:has-next="$formHasNext"
:can-delete="$formCanDelete"
>
<acun:input wire:model="name" label="Ad" required />
</acun:form-modal>
</div>
Bileşeni bir sayfaya <livewire:customers.customer-form /> ile koyun. Düğmeye basınca pencere açılır. Altında altı düğmeli işlem çubuğu kendiliğinden gelir: Önceki, Sonraki, Ekle, Sil, Vazgeç ve Kaydet. Düğmeler gizlenmez; o an kullanılamayanlar pasif olur (disabled ve aria-disabled). Kaydet yalnızca bir alan değişince açılır. Değişiklik varken pencereyi kapatmak isterseniz bir uyarı çıkar.
Açık durumunu bağlamak
Yukarıdaki örnekte wire:model="showForm" pencerenin açık olup olmadığını bir Livewire özelliğine bağlar. Pencere her çizimde bu özelliğin değerine göre açılır ya da kapanır. Tarayıcıda kapatılınca (X, Vazgeç, Esc, arka plan) özellik false yapılır.
wire:model vermezseniz pencere yalnızca tarayıcıda gizlenir. Bileşenin bir sonraki isteği onu yeniden gösterir. Bağlamadan kendiniz yönetmek isterseniz ui:form-close-approved olayını dinleyin: x-on:ui:form-close-approved="$wire.showForm = false".
Sık kullanılan seçenekler
İşlem çubuğu (acun:form-actions) varsayılan olarak previous, next, createNew ve delete metotlarını çağırır. Başka adlar için pencerenin actions slotuna kendi çubuğunuzu koyun:
<acun:form-modal id="customer-form" title="Müşteri" wire:model="showForm" wire:submit="save">
…
<acun:slot:actions>
<acun:form-actions previous="previousCustomer" next="nextCustomer" create="createNew" delete="remove" />
</acun:slot:actions>
</acun:form-modal>
| Prop | Ne yapar | Varsayılan |
|---|---|---|
previous, next |
Önceki ve Sonraki düğmelerinin çağırdığı metot | previous, next |
create, delete |
Ekle ve Sil düğmelerinin metodu; Sil önce onay sorar | createNew, delete |
delete-confirmation |
Silme onayının metni | "Bu kaydı silmek istediğinize emin misiniz?" |
cancel |
Vazgeç düğmesi; bir metot adı verilirse o metodu çağırır | true |
save |
Yüklenirken Kaydet düğmesini pasif tutan metot (wire:target) |
save |
sticky |
Çubuğu ekranın altında tutar | false |
Kaydet düğmesi her zaman formu gönderir; çağrılan metodu formdaki wire:submit belirler.
Sayfadaki formda işlem çubuğu
acun:form-actions pencere dışında da çalışır. Bir formun içine konunca kendini o forma bağlar:
<acun:form id="product-form" wire:submit="save" dirty-check>
<acun:input wire:model="name" label="Ürün adı" required />
<acun:form-actions sticky cancel="discard">
<p class="text-xs text-gray-500">{{ $name }}</p>
</acun:form-actions>
</acun:form>
id: Livewire'dakiformSaved('product-form')gibi çağrıların formu bulması için formaidverin.data-ui-dirty-formya dadata-form-ideklemeniz gerekmez.- Vazgeç: Sayfada formu son temiz değerlerine döndürür. Buna
wire:modeldeğerleri ile seçim kutusu, etiket girişi, puan ve kod kutuları dahildir. Pencerede ise Vazgeç pencereyi kapatır. cancel="discard": Vazgeç bunun yerine o Livewire metodunu çağırır. Metottan sonra çizilen değerler yeni temiz durum sayılır. Doğrulama hatalarını da temizlemek için kullanışlıdır.sticky: Çubuk ekranın altında kalır. Formun sonunda kendi yerini kapladığı için yapıştığında sayfada hiçbir şey kaymaz. Tema zemini üzerinde gölgeli bir kart olarak görünür.- Slot: Verilirse çubuğun başında durum satırı olarak gösterilir.
dirty-check: Sayfadan ayrılma uyarısını açar. Uyarı, çubuğun kaydettiği temiz değerleri başlangıç kabul eder; ayrıcaform-savedgöndermeniz gerekmez.
Livewire bileşeni
Kayıt düzenlemek ve silmek için en kısa örnekteki bileşene bir kayıt kimliği ve şu metotları ekleyin:
use Livewire\Attributes\Locked;
// Yalnızca sunucu değiştirir; tarayıcıdan gelen $wire.set('recordId', …) reddedilir.
#[Locked]
public ?int $recordId = null;
public function edit(Customer $customer): void
{
$this->authorize('view', $customer);
$this->name = $customer->name;
$this->recordId = $customer->id;
$this->showForm = true;
$this->formRecordLoaded('customer-form', canDelete: auth()->user()->can('delete', $customer));
}
public function save(): void
{
$customer = $this->recordId ? Customer::findOrFail($this->recordId) : new Customer;
$this->authorize($customer->exists ? 'update' : 'create', $customer->exists ? $customer : Customer::class);
$this->formSaving('customer-form');
try {
$customer->fill($this->validate(['name' => ['required', 'string', 'max:120']]))->save();
} catch (ValidationException $exception) {
$this->formSaveFailed('customer-form');
throw $exception;
}
$this->recordId = $customer->id;
$this->formSaved('customer-form', canDelete: auth()->user()->can('delete', $customer));
}
public function delete(): void
{
// $formCanDelete yalnızca düğmeyi açar; yetki burada yeniden denetlenir.
$customer = Customer::findOrFail($this->recordId);
$this->authorize('delete', $customer);
$customer->delete();
$this->createNew();
}
Bu save() kayıttan sonra pencereyi açık bırakır; çubukta Ekle ve Sil açılır. createNew() içinde kimliği de sıfırlayın: $this->reset(['name', 'recordId']);. Bir kaydı açan düğme: wire:click="edit({{ $customer->id }})".
Trait'in metotları düğmelerin durumunu tarayıcıya bildirir. İlk parametre formun id değeridir:
| Metot | Ne zaman çağrılır |
|---|---|
formCreating($form) |
Yeni kayıt için alanları sıfırladıktan sonra |
formRecordLoaded($form, hasPrevious:, hasNext:, canDelete:) |
Bir kaydı forma yükledikten sonra |
formSaving($form) |
Kaydetmeden hemen önce; düğmeler kilitlenir |
formSaved($form, hasPrevious:, hasNext:, canDelete:) |
Kayıt başarılı olunca; form temiz sayılır |
formSaveFailed($form) |
Doğrulama hatasında; form değişmiş olarak kalır |
Önceki ve Sonraki için previous() ve next() metotlarını siz yazarsınız. Kaydı yükledikten sonra formRecordLoaded() çağrısına hasPrevious: ve hasNext: değerlerini verin.
Dikkat
Kayıt kimliği (
recordId) mutlaka#[Locked]olmalıdır. Aksi hâlde kullanıcı tarayıcıdan başka bir kaydın kimliğini verip onu kaydedebilir ya da silebilir.
edit() gibi public metotlar tarayıcıdan her kimlikle çağrılabilir. Bu yüzden yüklerken, kaydederken ve silerken yetkiyi her seferinde sunucuda, yetki kuralıyla (policy) denetleyin. Trait'in formMode, formRecordExists, formHasPrevious, formHasNext ve formCanDelete alanları da #[Locked]tır. Bu alanlar yalnızca düğmelerin durumunu belirler; sunucudaki bir işleme bunlara bakarak izin vermeyin.
Kaydedilmemiş değişiklik uyarısı
Temiz bir form X, Vazgeç, Esc ya da arka plana tıklanınca doğrudan kapanır. Formda değişiklik varsa önce bir uyarı penceresi açılır:
- Kal: Forma döner; odak yerinde kalır.
- Değişiklikleri kaybet: Alanları son temiz değerlerine döndürür, sonra pencereyi kapatır ya da gezinmeye devam eder.
Değişiklik varken kullanıcı sayfayı yenilerse, sekmeyi kapatırsa ya da tarayıcının geri ve ileri düğmelerini kullanırsa tarayıcının kendi uyarısı çıkar. wire:navigate bağlantıları aynı uyarı penceresini açar.
Pencerede olmayan bir form için acun:form bileşenine dirty-check ekleyin:
<acun:form wire:submit="save" dirty-check>
…
</acun:form>
Kullanıcı wire:navigate ile ayrılmak isterse onay penceresi (acun:form-guard-dialog) açılır. Temanın düzenleri bu pencereyi zaten içerir.
Olaylar
Form, durumu değiştikçe tarayıcıda olay yayınlar. Örneğin bir "Kaydedilmemiş değişiklik var" yazısı için:
<acun:form id="product-form" wire:submit="save" dirty-check
x-data="{ dirty: false }" x-on:ui:dirty="dirty = true" x-on:ui:clean="dirty = false">
…
<acun:form-actions sticky>
<span wire:ignore x-text="dirty ? 'Kaydedilmemiş değişiklik var' : 'Tüm değişiklikler kaydedildi'"></span>
</acun:form-actions>
</acun:form>
| Olay | Ne zaman |
|---|---|
ui:form-mounted |
Form ilk kez tanındığında |
ui:dirty, ui:clean |
Form değiştiğinde; yeniden temiz olduğunda |
ui:record-loaded, ui:form-reset |
Kayıt yüklendiğinde; yeni kayıt başladığında |
ui:form-mode-changed |
Durum değiştiğinde, ör. kaydedilirken |
ui:form-saved, ui:form-save-failed |
Kayıt başarılı ya da başarısız olduğunda |
ui:form-discard |
Değişiklikler geri alındığında |
ui:form-close-request |
Pencereyi kapatmak için siz gönderirsiniz |
ui:form-close-approved |
Pencere kapandığında |
Olaylar yukarı doğru yayılır (bubbling); ayrıntıda formun id değeri ve durumu bulunur. Pencereyi kendi kodunuzla kapatırken de aynı korumadan geçin:
document.querySelector('[data-form-id="customer-form"]')
.dispatchEvent(new CustomEvent('ui:form-close-request', { bubbles: true }));
Değişiklik varsa uyarı yine çıkar.
İleri düzey
Düğmeler ne zaman açılır
Form her an bir durumdadır: initial (ilk açılış), viewing (kayıtlı bir kayıt), editing (kayıt değiştiriliyor), creating (yeni kayıt) ya da saving (kaydediliyor). Düğmeler bu duruma ve formda değişiklik olup olmadığına göre açılır:
| Düğme | Ne zaman açık |
|---|---|
| Önceki, Sonraki | Kayıtlı bir kayıt temizken ve önceki ya da sonraki kayıt varken |
| Ekle | Kayıtlı bir kayıt temizken |
| Sil | Kayıtlı bir kayıt temizken ve silme yetkisi varken |
| Kaydet | Formda değişiklik varken |
| Vazgeç | Her zaman |
İlk kayıt başarıyla kaydedilmeden Ekle hiçbir zaman açılmaz. Kaydedilirken Vazgeç dahil bütün düğmeler pasiftir.
Değişiklik nasıl anlaşılır
Form, son temiz değerlerle şimdiki değerleri karşılaştırır. Kullanıcı bir değeri değiştirip eski hâline getirirse form yeniden temiz olur.
- Desteklenen alanlar: text, email, number, date, datetime-local, time, hidden, checkbox, radio, textarea, select, çoklu select ve file.
- Aynı adı taşıyan alanlar (
etiketler[]gibi) liste olarak karşılaştırılır. - Gerçek alanı olmayan bileşenler (
acun:combobox,acun:tag-input,acun:rating,acun:input-otp)wire:modelile bağlanınca değerlerini özelliğin adıyla gizli alanlarda tutar. Kullanıcı değiştirincechangeolayı gönderirler. Bu bileşenlerin arama ve yazı kutuları ile kod kutularıdata-dirty-ignoretaşır. - Dosya alanlarında yalnızca ad, boyut, MIME türü ve son değiştirilme zamanı karşılaştırılır.
- Sonradan eklenen alanlar ve tekrarlanan satırlar da izlenir (
MutationObserver).
Bir alanı ya da kapsayıcıyı takip dışı bırakmak için data-dirty-ignore ekleyin. acun:form dirty-check koruması da bu niteliğe uyar:
<input type="search" data-dirty-ignore>
Temiz değerler şu anlarda yeniden alınır: ilk açılış, başarılı kayıt, sıfırlama ya da geri alma ve kayıt yükleme. Livewire sayfayı güncellerken (morph) temiz değerler formun id değeriyle korunur.
Temiz değerler, Livewire yeni değerleri alanlara yerleştirdikten sonra alınır. ui:record-loaded, ui:form-saved ve ui:form-reset olayları güncellemeden hemen sonra gelir; wire:model değerleri ise bir an sonra. Sayfa ilk açıldığında da Alpine alanları doldurana kadar beklenir. Doğrulama ya da ağ hatasında temiz değerler değişmez ve form değişmiş olarak kalır. Aynı sayfadaki her form id değeriyle ayrı yönetilir.
JavaScript arayüzü
Arayüz bilinçli olarak küçüktür. Yönetici ayrı bir parçadır; yalnızca form penceresi ya da işlem çubuğu olan sayfada yüklenir. Kendi kodunuzda loadDirtyForms() ile alın:
import { loadDirtyForms } from '@acunsoft/acun-ui-pro';
const { initDirtyForms, UiDirtyFormManager, deriveActionState } = await loadDirtyForms();
const manager = initDirtyForms();
manager.hasDirtyForms();
manager.requestNavigation('/hedef');
Daha fazlası
- Çalışan örnek: demo uygulamasında Formlar › Form düzenleri › Sabit düğmeli form.
- Liste sayfası: kayıtları listelemek ve formu listeden açmak.
- Sık sorulan sorular: uyarı çıkmıyorsa ne kontrol edilir.