Belajar Livewire - Forms & Validation
Episode 9 of 23

Belajar Livewire - Forms & Validation

Membangun form interaktif di Livewire: state dengan property per field, aturan validasi #[Validate] dan validate(), validasi real-time dengan validateOnly, custom error messages, dan pola multi-step forms untuk alur input yang panjang.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Form adalah jantung aplikasi web — dan di sinilah Livewire menunjukkan keunggulannya paling terlihat. Validasi yang biasanya membutuhkan duplikasi (JS untuk real-time, PHP untuk server) kini ditulis sekali di PHP, dan error bisa ditampilkan real-time tanpa reload. Di episode ini kita membangun fondasi form: state, aturan validasi, feedback, dan multi-step.

Kunci pemahamannya: karena state form hidup di server, validasi PHP bisa dipanggil kapan saja — bahkan per field — tanpa mengirim seluruh form. Inilah yang memungkinkan validasi real-time yang benar-benar aman (tidak hanya tampilan).

State Form: Property per Field

Setiap field input menjadi public property di komponen:

app/Livewire/CreatePost.php
<?php
 
namespace App\Livewire;
 
use Livewire\Component;
 
class CreatePost extends Component
{
    public string $title = '';
    public string $body = '';
    public string $status = 'draft';
 
    public function save()
    {
        // validasi + simpan
    }
}
resources/views/livewire/create-post.blade.php
<form wire:submit="save">
    <div>
        <label for="title">Judul</label>
        <input type="text" id="title" wire:model="title">
    </div>
 
    <div>
        <label for="body">Isi</label>
        <textarea id="body" wire:model="body"></textarea>
    </div>
 
    <div>
        <label for="status">Status</label>
        <select id="status" wire:model="status">
            <option value="draft">Draft</option>
            <option value="published">Published</option>
        </select>
    </div>
 
    <button type="submit">Simpan</button>
</form>

wire:submit="save" mengirim seluruh state form ke save() dalam satu request — di v4, field wire:model default deferred ikut tersinkron pada saat itu juga.

Menambahkan Aturan Validasi

Ada dua gaya: attribute #[Validate] di property, atau method rules() + panggilan validate().

Attribute #[Validate]

Validasi lewat attribute
use Livewire\Attributes\Validate;
 
class CreatePost extends Component
{
    #[Validate('required|min:3|max:255')]
    public string $title = '';
 
    #[Validate('required|min:10')]
    public string $body = '';
}

Aturan ini otomatis aktif saat $this->validate() dipanggil:

Method save dengan validate
public function save()
{
    $this->validate();
 
    Post::create([
        'title' => $this->title,
        'body' => $this->body,
        'status' => $this->status,
    ]);
 
    session()->flash('status', 'Post berhasil dibuat');
    $this->reset();
}

Method rules()

Alternatifnya, deklarasikan aturan dalam method dan panggil validate():

Validasi lewat method rules
public function rules()
{
    return [
        'title' => ['required', 'min:3', 'max:255'],
        'body' => ['required', 'min:10'],
    ];
}
 
public function save()
{
    $this->validate();
    // simpan...
}

rules() lebih fleksibel untuk aturan dinamis (misalnya 'required' hanya jika status tertentu). Saat validasi gagal, Livewire melempar ValidationException, membatalkan aksi, dan mengisi error bag — tanpa reload.

Menampilkan Error di View

Error tersedia lewat helper $errors di view:

Menampilkan error per field
<div>
    <label for="title">Judul</label>
    <input type="text" id="title" wire:model="title">
    @error('title')
        <span class="text-red-600">{{ $message }}</span>
    @enderror
</div>

Bisa juga lewat direktif @error biasa yang sudah didukung Laravel — Livewire mengisi error bag dengan format standar sehingga keduanya bekerja.

Validasi Real-Time dengan validateOnly

Untuk feedback langsung saat user mengisi field, panggil validasi per-field pada hook updated:

Validasi per field
public function updatedTitle($value)
{
    $this->validateOnly('title');
}

validateOnly('title') memvalidasi hanya satu field dan mengembalikan error-nya. Dikombinasikan dengan wire:model.live pada input, error muncul saat user selesai mengetik field itu — tanpa menunggu submit:

Validasi real-time
<input type="text" wire:model.live="title">

Warning

validateOnly memvalidasi satu field — jangan pakai untuk menampilkan semua error form saat user baru menyentuh satu field. Simpan validateOnly di hook updated* untuk feedback per-field, dan biarkan validate() penuh dijalankan saat submit.

Custom Error Messages

Aturan Laravel punya pesan default yang generik. Ganti dengan pesan yang lebih manusiawi:

Custom messages di rules()
public function rules()
{
    return [
        'title' => ['required', 'min:3'],
    ];
}
 
public function messages()
{
    return [
        'title.required' => 'Judul wajib diisi.',
        'title.min' => 'Judul minimal :min karakter.',
    ];
}

Atau gabungkan aturan dan pesan dalam satu method:

Rules dengan pesan inline
public function validateTitle()
{
    $this->validate([
        'title' => 'required|min:3',
    ], [
        'title.required' => 'Judul wajib diisi.',
    ]);
}

Multi-Step Forms

Form panjang dipecah menjadi langkah-langkah. Kuncinya: simpan indeks langkah dan kondisi render per langkah, lalu validasi parsial di tiap langkah:

State multi-step
public int $step = 1;
 
public function next()
{
    $rules = match ($this->step) {
        1 => ['title' => 'required|min:3', 'body' => 'required'],
        2 => ['category' => 'required'],
        default => [],
    };
 
    $this->validate($rules);
    $this->step++;
}
 
public function back()
{
    $this->step = max(1, $this->step - 1);
}
Tampilan per langkah
@if ($step === 1)
    <input type="text" wire:model="title">
    <textarea wire:model="body"></textarea>
@elseif ($step === 2)
    <select wire:model="category">
        <!-- opsi kategori -->
    </select>
@endif
 
<button type="button" wire:click="back">Kembali</button>
<button type="button" wire:click="next">Lanjut</button>

Setiap langkah hanya memvalidasi field miliknya — jadi user tidak dihujani error field yang belum mereka lihat. Perhatikan tiap langkah dipertahankan sebagai state, jadi kembali ke langkah sebelumnya tidak kehilangan data.

Pitfall Umum

  • Lupa wire:submit: submit biasa me-reload halaman dan state hilang (episode 5).
  • Validasi hanya di client-side: jangan percaya validasi JS; validate() di PHP tetap wajib.
  • validateOnly untuk semua field: boros dan bikin pengalaman aneh. Pakai per-field di hook.
  • Field tanpa default value: property kosong bernilai null, aturan required akan menangkapnya dengan pesan membingungkan. Set default '' untuk field teks.
  • Tidak mereset form setelah sukses: gunakan $this->reset() agar form bersih untuk input berikutnya.

Penutup

Inti yang harus dibawa pulang:

  • State form = property per field; wire:submit mengirim semua state ke action.
  • #[Validate] atau rules() + validate(); @error untuk menampilkan pesan.
  • validateOnly('field') di hook updated* untuk validasi real-time per field.
  • Custom messages via messages(); multi-step dengan indeks langkah + validasi parsial.
  • Selalu reset form setelah simpan sukses.

Di episode 10 selanjutnya kita akan membahas Form Objects (v4) — class Form khusus untuk merekap state dan aturan validasi, bind() untuk menghubungkan ke model, sehingga pola create/update bisa dipakai ulang dan tetap konsisten. Sampai jumpa di episode 10!

Belajar Livewire - Forms & Validation | Belajar Livewire