Belajar Livewire 4 #11 – Edit Page Component

Tutorial lengkap Livewire 4 dan Laravel 12 yang membahas konsep, fitur terbaru, dan praktik terbaik membangun aplikasi web modern tanpa ribet JavaScript. Materi disusun bertahap dari dasar hingga studi kasus nyata dengan penjelasan mengalir dan mudah dipahami.

✅ Telah dilihat 134 kali

Rating: 5.00 ⭐

... 04 February 2026, 06:27

Pada sesi kali ini, kita akan membuat page component yang berfungsi untuk menangani logika edit data (edit post) menggunakan Livewire 4 Single File Component.

Pada fitur edit ini, konsepnya kurang lebih sama dengan halaman create, bedanya:

  • Data sudah ada di database
  • Saat halaman dibuka, data tersebut ditampilkan kembali ke form
  • Ketika disimpan, data akan di-update, bukan ditambahkan

Livewire 4 sangat memudahkan proses ini karena class component dan Blade view berada dalam satu file, sehingga alur edit data bisa kita baca dari atas sampai bawah tanpa lompat-lompat file.


Membuat Page Component Edit

Pastikan teman-teman masih berada di direktori root project Laravel, lalu jalankan perintah berikut:

php artisan make:livewire pages::post.edit

Penjelasan Perintah

Mari kita bedah perintah ini pelan-pelan:

  • make:livewire Digunakan untuk membuat komponen Livewire baru
  • pages::post.edit Menandakan bahwa:
    • Komponen berada di folder pages/post
    • Nama file component adalah edit
    • Komponen ini difungsikan sebagai halaman edit post

Dengan pola penamaan ini, struktur project tetap konsisten:

  • index → list data
  • create → tambah data
  • edit → ubah data

Output yang Dihasilkan

Jika perintah berhasil dijalankan, maka akan muncul pesan seperti berikut:

INFO  Livewire component 
[C:\Users\LagiKoding\Desktop\simple-livewire4\resources\views/pages/post/⚡edit.blade.php] 
created successfully.

Apa Artinya?

Pesan tersebut menandakan bahwa:

  • File page component edit berhasil dibuat

  • Lokasi file berada di:

    resources/views/pages/post/⚡edit.blade.php
    

File inilah yang nantinya akan:

  • Menerima parameter id dari URL
  • Mengambil data post dari database
  • Menampilkan data ke dalam form
  • Menyimpan perubahan data ke database

Konfigurasi File edit.blade.php

Langkah selanjutnya, silakan teman-teman buka file berikut:

resources/views/pages/post/⚡edit.blade.php

Kemudian ubah menjadi seperti berikut ini:

<?php

use App\Models\Post;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;

new class extends Component
{
    use WithFileUploads;

    public Post $post;

    #[Validate('nullable|image|max:2048')]
    public $image;

    #[Validate('required|string')]
    public $title;

    #[Validate('required|string')]
    public $content;

    public function mount(Post $post)
    {
        $this->post    = $post;
        $this->title   = $post->title;
        $this->content = $post->content;
    }

    public function update()
    {
        $this->validate();

        if ($this->image) {
            $this->image->storeAs(
                'posts',
                $this->image->hashName(),
                'public'
            );

            $this->post->image = $this->image->hashName();
        }

        $this->post->update([
            'title'   => $this->title,
            'content' => $this->content,
        ]);

        session()->flash('message', 'Post berhasil diperbarui.');

        return $this->redirect('/posts', navigate: true);
    }

    public function render()
    {
        return $this->view()
            ->layout('layouts::app')
            ->title('Edit Post');
    }
};
?>

<div class="max-w-3xl mx-auto space-y-6">

    {{-- Header --}}
    <div>
        <h1 class="text-2xl font-semibold tracking-tight text-gray-900">
            Edit Post
        </h1>
        <p class="text-sm text-gray-600">
            Perbarui konten post dengan tampilan modern.
        </p>
    </div>

    {{-- Flash Message --}}
    @if (session()->has('message'))
        <div class="bg-green-500/10 border border-green-500/20
                    text-green-700 px-4 py-3 rounded-xl backdrop-blur">
            {{ session('message') }}
        </div>
    @endif

    {{-- Form Card --}}
    <div class="rounded-2xl bg-white/70 backdrop-blur-xl
                border border-white/30 shadow-xl p-6 md:p-8 space-y-6">

        <form wire:submit.prevent="update" class="space-y-6" enctype="multipart/form-data">

            {{-- Current Image --}}
            @if ($post->image)
                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-2">
                        Current Image
                    </label>

                    <img
                        src="{{ asset('storage/posts/' . $post->image) }}"
                        class="w-48 h-32 object-cover rounded-xl shadow"
                    >
                </div>
            @endif

            {{-- Upload New Image --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Replace Image (optional)
                </label>

                <input
                    type="file"
                    wire:model="image"
                    class="block w-full text-sm text-gray-700
                           file:mr-4 file:py-2 file:px-4
                           file:rounded-xl file:border-0
                           file:bg-indigo-500/10 file:text-indigo-700
                           hover:file:bg-indigo-500/20"
                >

                @error('image')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror

                {{-- Preview New Image --}}
                @if ($image)
                    <img
                        src="{{ $image->temporaryUrl() }}"
                        class="mt-4 w-48 h-32 object-cover rounded-xl shadow"
                    >
                @endif
            </div>

            {{-- Title --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Title
                </label>

                <input
                    type="text"
                    wire:model.defer="title"
                    class="w-full rounded-xl border border-white/40 bg-white/60
                           px-4 py-2 text-gray-900
                           focus:outline-none focus:ring-2 focus:ring-indigo-500/40"
                >

                @error('title')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror
            </div>

            {{-- Content --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Content
                </label>

                <textarea
                    wire:model.defer="content"
                    rows="6"
                    class="w-full rounded-xl border border-white/40 bg-white/60
                           px-4 py-2 text-gray-900
                           focus:outline-none focus:ring-2 focus:ring-indigo-500/40"
                ></textarea>

                @error('content')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror
            </div>

            {{-- Actions --}}
            <div class="flex items-center gap-3 pt-2">
                <button
                    type="submit"
                    class="inline-flex items-center px-5 py-2.5 rounded-xl
                           bg-indigo-600 text-white text-sm font-medium
                           hover:bg-indigo-700 transition shadow">
                    Update Post
                </button>

                <a
                    href="/posts"
                    wire:navigate
                    class="inline-flex items-center px-5 py-2.5 rounded-xl
                           bg-gray-500/80 text-white text-sm font-medium
                           hover:bg-gray-600 transition shadow">
                    Back
                </a>
            </div>

        </form>
    </div>
</div>

1. Bagian Class Component (Logika)

Bagian paling atas file adalah class Livewire, tempat semua logika aplikasi berada.

Import Class

use App\Models\Post;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;

Penjelasannya:

  • Post Digunakan untuk mengambil dan memperbarui data dari tabel posts
  • Component Class dasar Livewire
  • WithFileUploads Wajib digunakan karena halaman edit mendukung upload image
  • Validate Attribute validasi khas Livewire 4, lebih rapi dan modern

2. Properti Component

public Post $post;

Properti ini menampung model Post yang dikirim dari route. Livewire akan otomatis melakukan route model binding, sama seperti di controller Laravel.


Properti Form + Validasi

#[Validate('nullable|image|max:2048')]
public $image;

Artinya:

  • Image boleh kosong (karena edit)
  • Jika diisi, harus berupa gambar
  • Maksimal ukuran 2MB

#[Validate('required|string')]
public $title;
  • Title wajib diisi
  • Harus berupa string

#[Validate('required|string')]
public $content;
  • Konten juga wajib diisi

Dengan pendekatan ini, aturan validasi langsung menempel pada properti, sehingga kode lebih mudah dibaca dan dirawat.


3. Method mount() – Mengisi Data Awal

public function mount(Post $post)
{
    $this->post    = $post;
    $this->title   = $post->title;
    $this->content = $post->content;
}

Method mount() akan dijalankan saat halaman pertama kali dibuka.

Yang terjadi di sini:

  • Data post dari database ditangkap ke $this->post
  • Nilai title dan content diisi dengan data lama
  • Form otomatis terisi tanpa query tambahan

Inilah kenapa form edit langsung menampilkan data sebelumnya.


4. Method update() – Proses Update Data

$this->validate();

Saat tombol Update Post diklik:

  • Livewire menjalankan semua aturan validasi

Update Image (Opsional)

if ($this->image) {
    $this->image->storeAs(
        'posts',
        $this->image->hashName(),
        'public'
    );

    $this->post->image = $this->image->hashName();
}

Penjelasannya:

  • Jika user mengunggah gambar baru

  • File disimpan ke:

    storage/app/public/posts
    
  • Nama file dibuat unik menggunakan hashName()

  • Kolom image di model post diperbarui

Jika user tidak upload gambar, maka:

  • Image lama tetap dipakai
  • Tidak ada perubahan di kolom image

Update Data ke Database

$this->post->update([
    'title'   => $this->title,
    'content' => $this->content,
]);

Di sini kita hanya meng-update:

  • Title
  • Content

Sedangkan image:

  • Sudah di-handle terpisah
  • Lebih aman dan fleksibel

Flash Message & Redirect

session()->flash('message', 'Post berhasil diperbarui.');

Digunakan untuk menampilkan notifikasi sukses.

return $this->redirect('/posts', navigate: true);
  • Kembali ke halaman list post
  • Menggunakan navigasi Livewire (tanpa reload penuh)

5. Method render()

return $this->view()
    ->layout('layouts::app')
    ->title('Edit Post');

Artinya:

  • Menggunakan layout utama app
  • Judul halaman menjadi Edit Post

6. Bagian Tampilan (Blade View)

Setelah ?>, kita masuk ke bagian HTML + Tailwind CSS.


Header Halaman

<h1>Edit Post</h1>

Menandakan bahwa user sedang berada di halaman edit, bukan create.


Menampilkan Image Lama

@if ($post->image)
    <img src="{{ asset('storage/posts/' . $post->image) }}">
@endif

Fungsinya:

  • Menampilkan gambar lama
  • Memberi konteks ke user sebelum mengganti gambar

Upload Image Baru + Preview

<input type="file" wire:model="image">
  • Image baru opsional
  • Jika dipilih, Livewire langsung menyiapkan preview
@if ($image)
    <img src="{{ $image->temporaryUrl() }}">
@endif

Fitur ini sangat powerful:

  • Preview tanpa JavaScript
  • Tanpa upload ke server dulu

Input Title & Content

wire:model.defer

Artinya:

  • Nilai dikirim saat submit
  • Lebih efisien dibanding wire:model

Tombol Aksi

  • Update Post → menjalankan method update()
  • Back → kembali ke halaman /posts tanpa reload

Konfigurasi Rute

Langkah berikutnya adalah menghubungkan page component Livewire Edit Post ke sistem routing Laravel.

Silakan teman-teman buka file:

routes/web.php

Lalu sesuaikan menjadi seperti berikut:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::livewire('/posts', 'pages::post.index');
Route::livewire('/posts/create', 'pages::post.create');
Route::livewire('/posts/{post}/edit', 'pages::post.edit');

Penjelasan

Route Edit Post

Route::livewire('/posts/{post}/edit', 'pages::post.edit');

Artinya:

  • URL:

    /posts/{post}/edit
    
  • {post} adalah route model binding, Laravel akan otomatis:

    • Mengambil data Post berdasarkan id
    • Mengirimkannya ke Livewire component
  • Akan menampilkan page component:

    resources/views/pages/post/⚡edit.blade.php
    

Jika kita buka halaman edit dengan cara klik salah satu teks edit pada data post, maka akan terlihat seperti berikut ini:

Pada materi berikutnya, kita akan membuat halaman hompage untuk menampilkan daftar data Posts kedalam bentuk card.

Daftar eBook