TR
Başlangıç

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.css iç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'daki formSaved('product-form') gibi çağrıların formu bulması için forma id verin. data-ui-dirty-form ya da data-form-id eklemeniz gerekmez.
  • Vazgeç: Sayfada formu son temiz değerlerine döndürür. Buna wire:model değ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ıca form-saved gö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:model ile bağlanınca değerlerini özelliğin adıyla gizli alanlarda tutar. Kullanıcı değiştirince change olayı gönderirler. Bu bileşenlerin arama ve yazı kutuları ile kod kutuları data-dirty-ignore taşı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.
Acun UIİnsanlar için tasarlandı.