Javascript
Menggunakan JavaScript dalam komponen Livewire
Livewire dan Alpine menyediakan banyak utilitas untuk membangun komponen dinamis langsung di HTML Anda, namun terkadang ada saatnya yang membantu untuk keluar dari HTML dan mengeksekusi JavaScript biasa untuk komponen Anda. Direktif @script dan @assets Livewire memungkinkan Anda untuk melakukan ini dengan cara yang dapat diprediksi dan dapat dipelihara.
Menjalankan script
Untuk mengeksekusi JavaScript khusus dalam komponen Livewire Anda, cukup bungkus elemen <script> dengan @script dan @endscript. Ini akan memberi tahu Livewire untuk menangani eksekusi JavaScript ini.
Karena script di dalam @script ditangani oleh Livewire, mereka dieksekusi pada waktu yang sempurna setelah halaman dimuat, tetapi sebelum komponen Livewire dirender. Ini berarti Anda tidak perlu lagi membungkus script Anda dalam document.addEventListener('...') untuk memuatnya dengan benar.
Ini juga berarti bahwa komponen Livewire yang dimuat secara lambat atau bersyarat masih dapat mengeksekusi JavaScript setelah halaman diinisialisasi.
<div>
...
</div>
@script
<script>
// JavaScript ini akan dieksekusi setiap kali komponen ini dimuat ke halaman...
</script>
@endscript
Berikut adalah contoh yang lebih lengkap di mana Anda dapat melakukan sesuatu seperti mendaftarkan aksi JavaScript yang digunakan dalam komponen Livewire Anda.
<div>
<button wire:click="$js.increment">+</button>
</div>
@script
<script>
$js('increment', () => {
console.log('increment')
})
</script>
@endscript
Untuk mempelajari lebih lanjut tentang aksi JavaScript, kunjungi dokumentasi aksi.
Menggunakan $wire dari script
Fitur bermanfaat lain dari menggunakan @script untuk JavaScript Anda adalah Anda secara otomatis memiliki akses ke objek $wire komponen Livewire Anda.
Berikut adalah contoh penggunaan setInterval sederhana untuk me-refresh komponen setiap 2 detik (Anda dapat dengan mudah melakukan ini dengan wire:poll, tetapi ini adalah cara sederhana untuk mendemonstrasikan poinnya):
Anda dapat mempelajari lebih lanjut tentang $wire di dokumentasi $wire.
@script
<script>
setInterval(() => {
$wire.$refresh()
}, 2000)
</script>
@endscript
Mengevaluasi ekspresi JavaScript sekali pakai
Selain menunjuk seluruh metode untuk dievaluasi dalam JavaScript, Anda dapat menggunakan metode js() untuk mengevaluasi ekspresi individu yang lebih kecil di backend.
Ini umumnya berguna untuk melakukan semacam tindak lanjut di sisi klien setelah aksi sisi server dilakukan.
Sebagai contoh, berikut adalah contoh komponen CreatePost yang memicu dialog peringatan di sisi klien setelah post disimpan ke database:
<?php
namespace App\Livewire;
use Livewire\Component;
class CreatePost extends Component
{
public $title = '';
public function save()
{
// ...
$this->js("alert('Post saved!')"); // [tl! highlight:6]
}
}
Ekspresi JavaScript alert('Post saved!') sekarang akan dieksekusi di klien setelah post disimpan ke database di server.
Anda dapat mengakses objek $wire komponen saat ini di dalam ekspresi.
Memuat assets
Direktif @script berguna untuk mengeksekusi sedikit JavaScript setiap kali komponen Livewire dimuat, namun terkadang Anda mungkin ingin memuat seluruh script dan style assets di halaman bersama dengan komponen.
Berikut adalah contoh penggunaan @assets untuk memuat library date picker yang disebut Pikaday dan menginisialisasinya di dalam komponen Anda menggunakan @script:
<div>
<input type="text" data-picker>
</div>
@assets
<script src="https://cdn.jsdelivr.net/npm/pikaday/pikaday.js" defer></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css">
@endassets
@script
<script>
new Pikaday({ field: $wire.$el.querySelector('[data-picker]') });
</script>
@endscript
Ketika komponen ini dimuat, Livewire akan memastikan setiap @assets dimuat di halaman itu sebelum mengevaluasi @script. Selain itu, ini akan memastikan @assets yang disediakan hanya dimuat sekali per halaman tidak peduli berapa banyak instance dari komponen ini ada, tidak seperti @script, yang akan dievaluasi untuk setiap instance komponen di halaman.
Event Livewire global
Livewire mengirimkan dua event browser yang membantu untuk Anda mendaftarkan titik ekstensi khusus apa pun dari script luar:
<script>
document.addEventListener('livewire:init', () => {
// Berjalan setelah Livewire dimuat tetapi sebelum diinisialisasi
// di halaman...
})
document.addEventListener('livewire:initialized', () => {
// Berjalan segera setelah Livewire selesai menginisialisasi
// di halaman...
})
</script>
[!info] Seringkali bermanfaat untuk mendaftarkan direktif khusus atau lifecycle hooks di dalam
livewire:initsehingga mereka tersedia sebelum Livewire mulai menginisialisasi di halaman.
Objek global Livewire
Objek global Livewire adalah titik awal terbaik untuk berinteraksi dengan Livewire dari script eksternal.
Anda dapat mengakses objek JavaScript global Livewire di window dari mana saja di dalam kode sisi klien Anda.
Seringkali membantu untuk menggunakan window.Livewire di dalam listener event livewire:init
Mengakses komponen
Anda dapat menggunakan metode berikut untuk mengakses komponen Livewire spesifik yang dimuat di halaman saat ini:
// Ambil objek $wire untuk komponen pertama di halaman...
let component = Livewire.first()
// Ambil objek `$wire` komponen yang diberikan berdasarkan ID-nya...
let component = Livewire.find(id)
// Ambil array objek `$wire` komponen berdasarkan nama...
let components = Livewire.getByName(name)
// Ambil objek $wire untuk setiap komponen di halaman...
let components = Livewire.all()
[!info] Setiap metode ini mengembalikan objek
$wireyang mewakili status komponen di Livewire.
Anda dapat mempelajari lebih lanjut tentang objek ini di dokumentasi
$wire.
Berinteraksi dengan event
Selain mengirimkan dan mendengarkan event dari komponen individual di PHP, objek global Livewire memungkinkan Anda untuk berinteraksi dengan sistem event Livewire dari mana saja di aplikasi Anda:
// Kirim event ke komponen Livewire yang mendengarkan...
Livewire.dispatch('post-created', { postId: 2 })
// Kirim event ke komponen Livewire tertentu berdasarkan nama...
Livewire.dispatchTo('dashboard', 'post-created', { postId: 2 })
// Dengarkan event yang dikirim dari komponen Livewire...
Livewire.on('post-created', ({ postId }) => {
// ...
})
Dalam skenario tertentu, Anda mungkin perlu membatalkan pendaftaran event Livewire global. Misalnya, saat bekerja dengan komponen Alpine dan wire:navigate, beberapa listener mungkin didaftarkan saat init dipanggil saat menavigasi antar halaman. Untuk mengatasi ini, manfaatkan fungsi destroy, yang secara otomatis dipanggil oleh Alpine. Ulangi semua listener Anda di dalam fungsi ini untuk membatalkan pendaftarannya dan mencegah akumulasi yang tidak diinginkan.
Alpine.data('MyComponent', () => ({
listeners: [],
init() {
this.listeners.push(
Livewire.on('post-created', (options) => {
// Lakukan sesuatu...
})
);
},
destroy() {
this.listeners.forEach((listener) => {
listener();
});
}
}));
Menggunakan lifecycle hooks
Livewire memungkinkan Anda untuk mengaitkan ke berbagai bagian dari lifecycle globalnya menggunakan Livewire.hook():
// Daftarkan callback untuk dieksekusi pada hook internal Livewire tertentu...
Livewire.hook('component.init', ({ component, cleanup }) => {
// ...
})
Informasi lebih lanjut tentang hooks JavaScript Livewire dapat ditemukan di bawah.
Mendaftarkan direktif khusus
Livewire memungkinkan Anda untuk mendaftarkan direktif khusus menggunakan Livewire.directive().
Di bawah ini adalah contoh direktif wire:confirm khusus yang menggunakan dialog confirm() JavaScript untuk mengonfirmasi atau membatalkan aksi sebelum dikirim ke server:
<button wire:confirm="Are you sure?" wire:click="delete">Delete post</button>
Berikut adalah implementasi wire:confirm menggunakan Livewire.directive():
Livewire.directive('confirm', ({ el, directive, component, cleanup }) => {
let content = directive.expression
// Objek "directive" memberi Anda akses ke direktif yang diurai.
// Misalnya, berikut adalah nilainya untuk: wire:click.prevent="deletePost(1)"
//
// directive.raw = wire:click.prevent
// directive.value = "click"
// directive.modifiers = ['prevent']
// directive.expression = "deletePost(1)"
let onClick = e => {
if (! confirm(content)) {
e.preventDefault()
e.stopImmediatePropagation()
}
}
el.addEventListener('click', onClick, { capture: true })
// Daftarkan kode cleanup apa pun di dalam `cleanup()` jika
// komponen Livewire dihapus dari DOM saat
// halaman masih aktif.
cleanup(() => {
el.removeEventListener('click', onClick)
})
})
Skema objek
Saat memperluas sistem JavaScript Livewire, penting untuk memahami objek berbeda yang mungkin Anda temui.
Berikut adalah referensi lengkap dari setiap properti internal Livewire yang relevan.
Sebagai pengingat, pengguna Livewire rata-rata mungkin tidak pernah berinteraksi dengan ini. Sebagian besar objek ini tersedia untuk sistem internal Livewire atau pengguna tingkat lanjut.
Objek $wire
Diberikan komponen Counter generik berikut:
<?php
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 1;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
Livewire mengekspos representasi JavaScript dari komponen sisi server dalam bentuk objek yang biasa disebut sebagai $wire:
let $wire = {
// Semua properti publik komponen dapat diakses langsung di $wire...
count: 0,
// Semua metode publik diekspos dan dapat dipanggil di $wire...
increment() { ... },
// Akses objek `$wire` komponen induk jika ada...
$parent,
// Akses elemen DOM root komponen Livewire...
$el,
// Akses ID komponen Livewire saat ini...
$id,
// Dapatkan nilai properti berdasarkan nama...
// Penggunaan: $wire.$get('count')
$get(name) { ... },
// Atur properti pada komponen berdasarkan nama...
// Penggunaan: $wire.$set('count', 5)
$set(name, value, live = true) { ... },
// Toggle nilai properti boolean...
$toggle(name, live = true) { ... },
// Panggil metode...
// Penggunaan: $wire.$call('increment')
$call(method, ...params) { ... },
// Definisikan aksi JavaScript...
// Penggunaan: $wire.$js('increment', () => { ... })
$js(name, callback) { ... },
// Hubungkan nilai properti Livewire dengan properti Alpine
// yang berbeda, sewenang-wenang...
// Penggunaan: <div x-data="{ count: $wire.$entangle('count') }">
$entangle(name, live = false) { ... },
// Pantau nilai properti untuk perubahan...
// Penggunaan: Alpine.$watch('count', (value, old) => { ... })
$watch(name, callback) { ... },
// Refresh komponen dengan mengirim commit ke server
// untuk me-render ulang HTML dan menukarnya ke halaman...
$refresh() { ... },
// Identik dengan `$refresh` di atas. Hanya nama yang lebih teknis...
$commit() { ... },
// Dengarkan event yang dikirim dari komponen ini atau turunannya...
// Penggunaan: $wire.$on('post-created', () => { ... })
$on(event, callback) { ... },
// Dengarkan hook lifecycle yang dipicu dari komponen ini atau request...
// Penggunaan: $wire.$hook('commit', () => { ... })
$hook(name, callback) { ... },
// Kirim event dari komponen ini...
// Penggunaan: $wire.$dispatch('post-created', { postId: 2 })
$dispatch(event, params = {}) { ... },
// Kirim event ke komponen lain...
// Penggunaan: $wire.$dispatchTo('dashboard', 'post-created', { postId: 2 })
$dispatchTo(otherComponentName, event, params = {}) { ... },
// Kirim event ke komponen ini dan tidak ada yang lain...
$dispatchSelf(event, params = {}) { ... },
// JS API untuk mengunggah file langsung ke komponen
// daripada melalui `wire:model`...
$upload(
name, // Nama properti
file, // Objek File JavaScript
finish = () => { ... }, // Berjalan ketika unggahan selesai...
error = () => { ... }, // Berjalan jika kesalahan dipicu saat unggahan...
progress = (event) => { // Berjalan saat unggahan berlangsung...
event.detail.progress // Bilangan bulat dari 1-100...
},
) { ... },
// API untuk mengunggah banyak file pada saat yang sama...
$uploadMultiple(name, files, finish, error, progress) { },
// Hapus unggahan setelah diunggah sementara tetapi tidak disimpan...
$removeUpload(name, tmpFilename, finish, error) { ... },
// Ambil objek "komponen" yang mendasarinya...
__instance() { ... },
}
Anda dapat mempelajari lebih lanjut tentang $wire di dokumentasi Livewire tentang mengakses properti dalam JavaScript.
Objek snapshot
Antara setiap permintaan jaringan, Livewire membuat serialisasi komponen PHP menjadi objek yang dapat dikonsumsi dalam JavaScript. Snapshot ini digunakan untuk membuat deserialisasi komponen kembali menjadi objek PHP dan karena itu memiliki mekanisme bawaan untuk mencegah perusakan:
let snapshot = {
// Status serialized komponen (properti publik)...
data: { count: 0 },
// Informasi jangka panjang tentang komponen...
memo: {
// ID unik komponen...
id: '0qCY3ri9pzSSMIXPGg8F',
// Nama komponen. Ex. <livewire:[name] />
name: 'counter',
// URI, metode, dan lokal halaman web yang
// komponen aslinya dimuat. Ini digunakan
// untuk menerapkan kembali middleware apa pun dari permintaan asli
// ke permintaan pembaruan komponen berikutnya (commits)...
path: '/',
method: 'GET',
locale: 'en',
// Daftar komponen "turunan" nested apa pun. Dikunci oleh
// ID template internal dengan ID komponen sebagai nilainya...
children: [],
// Apakah komponen ini dimuat "lazy loaded"...
lazyLoaded: false,
// Daftar kesalahan validasi apa pun yang dilemparkan selama
// permintaan terakhir...
errors: [],
},
// Hash terenkripsi aman dari snapshot ini. Dengan cara ini,
// jika pengguna jahat merusak snapshot dengan
// tujuan mengakses sumber daya yang tidak dimiliki di server,
// validasi checksum akan gagal dan kesalahan akan
// dilemparkan...
checksum: '1bc274eea17a434e33d26bcaba4a247a4a7768bd286456a83ea6e9be2d18c1e7',
}
Objek component
Setiap komponen di halaman memiliki objek komponen yang sesuai di balik layar yang melacak statusnya dan mengekspos fungsionalitas yang mendasarinya. Ini satu lapisan lebih dalam dari $wire. Ini hanya dimaksudkan untuk penggunaan tingkat lanjut.
Berikut adalah objek komponen aktual untuk komponen Counter di atas dengan deskripsi properti relevan dalam komentar JS:
let component = {
// Elemen HTML root komponen...
el: HTMLElement,
// ID unik komponen...
id: '0qCY3ri9pzSSMIXPGg8F',
// "nama" komponen (<livewire:[name] />)...
name: 'counter',
// Objek "effects" terbaru. Effects adalah "side-effects" dari
// perjalanan pulang-pergi server. Ini termasuk pengalihan, unduhan file, dll...
effects: {},
// status sisi server terakhir yang diketahui komponen...
canonical: { count: 0 },
// Objek data yang dapat diubah komponen yang mewakili
// status sisi klien yang langsung...
ephemeral: { count: 0 },
// Versi reaktif dari `this.ephemeral`. Perubahan ke
// objek ini akan diambil oleh ekspresi AlpineJS...
reactive: Proxy,
// Objek Proxy yang biasanya digunakan di dalam Alpine
// ekspresi sebagai `$wire`. Ini dimaksudkan untuk menyediakan
// antarmuka objek JS yang ramah untuk komponen Livewire...
$wire: Proxy,
// Daftar komponen "turunan" nested apa pun. Dikunci oleh
// ID template internal dengan ID komponen sebagai nilainya...
children: [],
// Representasi "snapshot" terakhir yang diketahui dari komponen ini.
// Snapshot diambil dari komponen sisi server dan digunakan
// untuk membuat ulang objek PHP di backend...
snapshot: {...},
// Versi yang tidak diurai dari snapshot di atas. Ini digunakan untuk mengirim kembali ke
// server pada perjalanan pulang-pergi berikutnya karena penguraian JS mengacaukan encoding PHP
// yang sering menghasilkan ketidakcocokan checksum.
snapshotEncoded: '{"data":{"count":0},"memo":{"id":"0qCY3ri9pzSSMIXPGg8F","name":"counter","path":"\/","method":"GET","children":[],"lazyLoaded":true,"errors":[],"locale":"en"},"checksum":"1bc274eea17a434e33d26bcaba4a247a4a7768bd286456a83ea6e9be2d18c1e7"}',
}
Payload commit
Ketika aksi dilakukan pada komponen Livewire di browser, permintaan jaringan dipicu. Permintaan jaringan itu berisi satu atau banyak komponen dan berbagai instruksi untuk server. Secara internal, payload jaringan komponen ini disebut "commits".
Istilah "commit" dipilih sebagai cara yang membantu untuk memikirkan hubungan Livewire antara frontend dan backend. Komponen dirender dan dimanipulasi di frontend hingga aksi dilakukan yang mengharuskannya untuk "melakukan commit" status dan pembaruannya ke backend.
Anda akan mengenali skema ini dari payload di tab jaringan DevTools browser Anda, atau hooks JavaScript Livewire:
let commit = {
// Objek snapshot...
snapshot: { ... },
// Daftar pasangan kunci-nilai dari properti
// untuk diperbarui di server...
updates: {},
// Array metode (dengan parameter) untuk dipanggil di sisi server...
calls: [
{ method: 'increment', params: [] },
],
}
Hooks JavaScript
Untuk pengguna tingkat lanjut, Livewire mengekspos sistem "hook" sisi klien internalnya. Anda dapat menggunakan hook berikut untuk memperluas fungsionalitas Livewire atau mendapatkan lebih banyak informasi tentang aplikasi Livewire Anda.
Inisialisasi komponen
Setiap kali komponen baru ditemukan oleh Livewire — baik pada beban halaman awal atau nanti — event component.init dipicu. Anda dapat mengaitkan ke component.init untuk mencegat atau menginisialisasi apa pun yang terkait dengan komponen baru:
Livewire.hook('component.init', ({ component, cleanup }) => {
//
})
Untuk informasi lebih lanjut, silakan konsultasikan dokumentasi pada objek komponen.
Inisialisasi elemen DOM
Selain memicu event saat komponen baru diinisialisasi, Livewire memicu event untuk setiap elemen DOM dalam komponen Livewire tertentu.
Ini dapat digunakan untuk menyediakan atribut HTML Livewire khusus dalam aplikasi Anda:
Livewire.hook('element.init', ({ component, el }) => {
//
})
Hooks DOM Morph
Selama fase morphing DOM — yang terjadi setelah Livewire menyelesaikan perjalanan pulang-pergi jaringan — Livewire memicu serangkaian event untuk setiap elemen yang dimutasi.
Livewire.hook('morph.updating', ({ el, component, toEl, skip, childrenOnly }) => {
//
})
Livewire.hook('morph.updated', ({ el, component }) => {
//
})
Livewire.hook('morph.removing', ({ el, component, skip }) => {
//
})
Livewire.hook('morph.removed', ({ el, component }) => {
//
})
Livewire.hook('morph.adding', ({ el, component }) => {
//
})
Livewire.hook('morph.added', ({ el }) => {
//
})
Selain event yang dipicu per elemen, event morph dan morphed dipicu untuk setiap komponen Livewire:
Livewire.hook('morph', ({ el, component }) => {
// Berjalan tepat sebelum elemen turunan dalam `component` di-morph
})
Livewire.hook('morphed', ({ el, component }) => {
// Berjalan setelah semua elemen turunan dalam `component` di-morph
})
Hooks Commit
Karena permintaan Livewire mengandung banyak komponen, request terlalu luas sebagai istilah untuk merujuk pada payload permintaan dan respons komponen individual. Sebaliknya, secara internal, Livewire merujuk pada pembaruan komponen sebagai commits — mengacu pada melakukan commit status komponen ke server.
Hook ini mengekspos objek commit. Anda dapat mempelajari lebih lanjut tentang skema mereka dengan membaca dokumentasi objek commit.
Menyiapkan commits
Hook commit.prepare akan dipicu segera sebelum permintaan dikirim ke server. Ini memberi Anda kesempatan untuk menambahkan pembaruan atau aksi menit terakhir apa pun ke permintaan keluar:
Livewire.hook('commit.prepare', ({ component }) => {
// Berjalan sebelum payload commit dikumpulkan dan dikirim ke server...
})
Mencegat commits
Setiap kali komponen Livewire dikirim ke server, commit dibuat. Untuk mengaitkan ke lifecycle dan konten commit individual, Livewire mengekspos hook commit.
Hook ini sangat kuat karena menyediakan metode untuk mengaitkan ke permintaan dan respons commit Livewire:
Livewire.hook('commit', ({ component, commit, respond, succeed, fail }) => {
// Berjalan segera sebelum payload commit dikirim ke server...
respond(() => {
// Berjalan setelah respons diterima tetapi sebelum diproses...
})
succeed(({ snapshot, effects }) => {
// Berjalan setelah respons berhasil diterima dan diproses
// dengan snapshot baru dan daftar effects...
})
fail(() => {
// Berjalan jika beberapa bagian permintaan gagal...
})
})
Hooks Request
Jika Anda lebih suka mengaitkan ke seluruh permintaan HTTP yang pergi dan kembali dari server, Anda dapat melakukannya menggunakan hook request:
Livewire.hook('request', ({ url, options, payload, respond, succeed, fail }) => {
// Berjalan setelah payload commit dikompilasi, tetapi sebelum permintaan jaringan dikirim...
respond(({ status, response }) => {
// Berjalan ketika respons diterima...
// "response" adalah objek respons HTTP mentah
// sebelum await response.text() dijalankan...
})
succeed(({ status, json }) => {
// Berjalan ketika respons diterima...
// "json" adalah objek respons JSON...
})
fail(({ status, content, preventDefault }) => {
// Berjalan ketika respons memiliki kode status kesalahan...
// "preventDefault" memungkinkan Anda menonaktifkan
// penanganan kesalahan default Livewire...
// "content" adalah konten respons mentah...
})
})
Menyesuaikan perilaku kedaluwarsa halaman
Jika dialog kedaluwarsa halaman default tidak cocok untuk aplikasi Anda, Anda dapat mengimplementasikan solusi khusus menggunakan hook request:
<script>
document.addEventListener('livewire:init', () => {
Livewire.hook('request', ({ fail }) => {
fail(({ status, preventDefault }) => {
if (status === 419) {
confirm('Your custom page expiration behavior...')
preventDefault()
}
})
})
})
</script>
Dengan kode di atas di aplikasi Anda, pengguna akan menerima dialog khusus ketika sesi mereka telah kedaluwarsa.