Menjembatani Livewire dengan JavaScript eksternal: $this->js() untuk mengeksekusi script dari server, $wire.$entangle untuk state dua arah, serta integrasi chart dan editor teks kaya seperti tiptap/ckeditor tanpa framework JavaScript.

Livewire menghilangkan kebutuhan menulis JavaScript untuk sebagian besar aplikasi — tetapi beberapa kebutuhan tetaplah JavaScript murni: chart interaktif, editor teks kaya, peta, atau library visualisasi. Kabar baiknya, Livewire punya jembatan resmi untuk itu: $this->js() untuk memanggil JavaScript dari server, dan $wire.$entangle untuk membagi state dua arah dengan Alpine.
Filosofinya tetap: PHP tetap di kursi pengemudi. Kalian memanggil JavaScript hanya sebagai alat tampilan, bukan sebagai logika aplikasi.
$this->js() mengeksekusi JavaScript di browser setelah aksi server selesai:
public function save()
{
// logika simpan...
$this->js("alert('Data tersimpan!')");
}Contoh yang lebih berguna — scroll ke elemen setelah render:
public function addComment()
{
$this->js(
"document.getElementById('comment-form').scrollIntoView({ behavior: 'smooth' })"
);
}Untuk kode yang lebih kompleks, gunakan Alpine dan panggil lewat event atau $wire:
$this->js('showToast("Post disimpan")');Catatan penting: string di $this->js() melewati JSON encoding, jadi aman dari XSS — jangan membangun string dengan interpolasi nilai user secara manual tanpa sanitasi.
Warning
$this->js() mengeksekusi kode di browser user. Jangan pernah menyisipkan nilai dari input user ke dalam string JavaScript tanpa escape — walau Livewire menangani banyak kasus, kebiasaan paling aman adalah memvalidasi dan sanitasi semua input yang akan masuk ke ekspresi JS.
$wire.$entangle() menghubungkan property Livewire dengan state Alpine secara dua arah — perubahan di sisi mana pun menyinkronkan ke sisi lain:
<div x-data="{ darkMode: $wire.$entangle('darkMode') }">
<button type="button" @click="darkMode = !darkMode">
<span x-show="darkMode">Mode terang</span>
<span x-show="!darkMode">Mode gelap</span>
</button>
</div>public bool $darkMode = false;Saat user mengklik, state Alpine darkMode berubah dan property Livewire $darkMode ikut berubah (dan sebaliknya). Ini pola utama untuk preferensi UI yang perlu disimpan di server namun diubah cepat di client. (Di Livewire v4, @entangle Blade directive lama sudah dihapus — gunakan $wire.$entangle.)
Chart library (Chart.js, ApexCharts, dsb.) biasanya butuh elemen DOM yang diinisialisasi sekali. Kombinasikan wire:ignore (episode 5) + $this->js():
<div wire:ignore>
<canvas id="revenue-chart" data-points='@json($chartData)'></canvas>
</div>use Livewire\Attributes\Computed;
#[Computed]
public function chartData()
{
return collect(Revenue::monthly())
->map(fn ($r) => [$r->month->format('M'), $r->total]);
}
public function mount()
{
$this->js("window.initRevenueChart('revenue-chart')");
}Pola kuncinya:
wire:ignore melindungi canvas dari morphing — Livewire tidak akan menyentuh DOM chart.$this->js() memanggil inisialisasi setelah komponen ter-render.data-points JSON, dibaca sekali oleh script inisialisasi.Saat data berubah dan perlu update, kirim event ke Alpine dan panggil method update chart — tanpa membangun ulang dari nol.
Editor seperti tiptap atau CKEditor mengelola kontennya sendiri di DOM. Pola amannya: simpan konten ke property Livewire saat editor berubah, dan jangan biarkan morphing menyentuh area editor:
<div x-data="{ body: $wire.$entangle('body') }"
wire:ignore
x-init="
new tiptap(this.$refs.editor, {
content: body,
onUpdate: ({ editor }) => body = editor.getHTML()
})
">
<div x-ref="editor"></div>
</div>$wire.$entangle('body') membuat konten editor dan property body saling sinkron.onUpdate menulis HTML ke body → state Livewire ikut.wire:ignore memastikan morphing tidak menimpa editor saat komponen me-render ulang.Dengan pola ini, konten editor bisa divalidasi dan disimpan sebagai data server biasa — tanpa menulis logika editor di PHP.
Prinsip umum untuk library pihak ketiga apa pun:
wire:ignore.$wire (dari Alpine ke Livewire) dan $this->js()/event (dari Livewire ke Alpine).Contoh untuk select2/tom-select (searchable dropdown):
<div wire:ignore x-data="{
init() {
new TomSelect(this.$refs.select, {
value: $wire.selected,
onChange: (value) => $wire.selected = value
})
}
}">
<select x-ref="select">
@foreach ($options as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@endforeach
</select>
</div>$wire.selected = value memperbarui property Livewire saat user memilih — dan karena wire:ignore, dropdown tidak rusak saat komponen merender ulang.
wire:ignore.$this->js() di mount() hanya sekali; jangan inisialisasi di setiap render.@entangle di v4: sudah dihapus — gunakan $wire.$entangle.Inti yang harus dibawa pulang:
$this->js() memanggil JavaScript dari server setelah aksi; aman dari XSS bila dipakai benar.$wire.$entangle membagi state dua arah antara Alpine dan Livewire.wire:ignore + inisialisasi sekali + $wire/$this->js() untuk komunikasi.Di episode 19 selanjutnya kita akan membahas Volt: functional API — menulis komponen dalam satu file .blade.php dengan new class extends Component, integrasi dengan Folio untuk file-based routing, serta perbandingan kapan memakai class-based vs Volt. Sampai jumpa di episode 19!