Slot
Halaman ini mengasumsikan Anda sudah membaca Dasar-Dasar Komponen. Bacalah itu terlebih dahulu jika Anda baru mengenal komponen.
Pada versi 2.6.0, kami memperkenalkan sintaks terpadu baru (direktif v-slot) untuk slot bernama dan slot dengan lingkup. Ini menggantikan atribut slot dan slot-scope, yang kini sudah usang, tetapi belum dihapus dan masih didokumentasikan di sini. Alasan pengenalan sintaks baru ini dijelaskan dalam RFC ini.
Konten Slot
Vue menerapkan API distribusi konten yang terinspirasi dari draf spesifikasi Web Components, menggunakan elemen <slot> sebagai saluran distribusi untuk konten. Ini memungkinkan Anda menyusun komponen seperti ini:
<navigation-link url="/profile">
Your Profile
</navigation-link>Kemudian dalam templat <navigation-link>, Anda mungkin memiliki:
<a v-bind:href="url" class="nav-link">
<slot></slot>
</a>Saat komponen dirender, <slot></slot> akan diganti dengan "Your Profile". Slot dapat berisi kode templat apa pun, termasuk HTML:
<navigation-link url="/profile">
<!-- Tambahkan ikon Font Awesome -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>Atau bahkan komponen lain:
<navigation-link url="/profile">
<!-- Gunakan komponen untuk menambahkan ikon -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>Jika templat <navigation-link> tidak mengandung elemen <slot>, konten apa pun yang disediakan di antara tag pembuka dan penutupnya akan dibuang.
Ruang Lingkup Kompilasi
Saat Anda ingin menggunakan data di dalam slot, seperti pada:
<navigation-link url="/profile">
Logged in as {{ user }}
</navigation-link>Slot tersebut memiliki akses ke properti instance yang sama (yaitu "ruang lingkup" yang sama) dengan sisa templat. Slot tidak memiliki akses ke ruang lingkup <navigation-link>. Misalnya, mencoba mengakses url tidak akan berhasil:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!-- The `url` will be undefined, because this content is passed _to_ <navigation-link>, rather than defined _inside_ the <navigation-link> component. -->
</navigation-link>Sebagai aturan, ingatlah:
Segala sesuatu dalam templat induk dikompilasi dalam ruang lingkup induk; segala sesuatu dalam templat anak dikompilasi dalam ruang lingkup anak.
Konten Cadangan
Ada kalanya berguna untuk menentukan konten cadangan (default) untuk sebuah slot, yang hanya akan dirender ketika tidak ada konten yang disediakan. Misalnya, dalam komponen <submit-button>:
<button type="submit">
<slot></slot>
</button>Kita mungkin ingin teks "Submit" dirender di dalam <button> sebagian besar waktu. Untuk menjadikan "Submit" sebagai konten cadangan, kita bisa meletakkannya di antara tag <slot>:
<button type="submit">
<slot>Submit</slot>
</button>Sekarang ketika kita menggunakan <submit-button> dalam komponen induk, tanpa menyediakan konten untuk slot:
<submit-button></submit-button>akan merender konten cadangan, "Submit":
<button type="submit">
Submit
</button>Tetapi jika kita menyediakan konten:
<submit-button>
Save
</submit-button>Maka konten yang disediakan akan dirender sebagai gantinya:
<button type="submit">
Save
</button>Slot Bernama
Diperbarui di 2.6.0+. Lihat di sini untuk sintaks usang menggunakan atribut slot.
Ada kalanya berguna untuk memiliki beberapa slot. Misalnya, dalam komponen <base-layout> dengan templat berikut:
<div class="container">
<header>
<!-- We want header content here -->
</header>
<main>
<!-- We want main content here -->
</main>
<footer>
<!-- We want footer content here -->
</footer>
</div>Untuk kasus seperti ini, elemen <slot> memiliki atribut khusus, name, yang dapat digunakan untuk mendefinisikan slot tambahan:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>Slot <slot> tanpa name secara implisit memiliki nama "default".
Untuk menyediakan konten ke slot bernama, kita dapat menggunakan direktif v-slot pada elemen <template>, dengan memberikan nama slot sebagai argumen v-slot:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>Sekarang semua yang ada di dalam elemen <template> akan diteruskan ke slot yang sesuai. Konten apa pun yang tidak dibungkus dalam <template> menggunakan v-slot dianggap sebagai konten untuk slot default.
Namun, Anda masih dapat membungkus konten slot default dalam <template> jika ingin eksplisit:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>Bagaimanapun, HTML yang dirender akan menjadi:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>Perhatikan bahwa v-slot hanya dapat ditambahkan ke <template> (dengan satu pengecualian), berbeda dengan atribut slot yang sudah usang.
Slot dengan Lingkup
Diperbarui di 2.6.0+. Lihat di sini untuk sintaks usang menggunakan atribut slot-scope.
Terkadang berguna bagi konten slot untuk memiliki akses ke data yang hanya tersedia di komponen anak. Misalnya, bayangkan komponen <current-user> dengan templat berikut:
<span>
<slot>{{ userme }}</slot>
</span>Kita mungkin ingin mengganti konten cadangan ini untuk menampilkan nama depan pengguna, bukan nama belakang, seperti ini:
<current-user>
{{ userame }}
</current-user>Namun, itu tidak akan berfungsi karena hanya komponen <current-user> yang memiliki akses ke user dan konten yang kami sediakan dirender di induk.
Untuk membuat user tersedia untuk konten slot di induk, kita dapat mengikat user sebagai atribut pada elemen <slot>:
<span>
<slot v-bind:user="user">
{{ userme }}
</slot>
</span>Atribut yang diikat ke elemen <slot> disebut properti slot. Sekarang, di ruang lingkup induk, kita dapat menggunakan v-slot dengan nilai untuk mendefinisikan nama untuk properti slot yang telah diberikan:
<current-user>
<template v-slot:default="slotProps">
{{ userame }}
</template>
</current-user>Dalam contoh ini, kami memilih untuk menamai objek yang berisi semua properti slot kami sebagai slotProps, tetapi Anda dapat menggunakan nama apa pun yang Anda suka.
Sintaks Singkat untuk Slot Default Tunggal
Dalam kasus seperti di atas, ketika hanya slot default yang diberikan konten, tag komponen dapat digunakan sebagai templat slot. Ini memungkinkan kita menggunakan v-slot langsung pada komponen:
<current-user v-slot:default="slotProps">
{{ userame }}
</current-user>Ini dapat diperpendek lebih lanjut. Sama seperti konten yang tidak ditentukan dianggap untuk slot default, v-slot tanpa argumen dianggap merujuk ke slot default:
<current-user v-slot="slotProps">
{{ userame }}
</current-user>Perhatikan bahwa sintaks singkat untuk slot default tidak dapat dicampur dengan slot bernama, karena akan menyebabkan ambiguitas lingkup:
<!-- INVALID, akan menghasilkan peringatan -->
<current-user v-slot="slotProps">
{{ userame }}
<template v-slot:other="otherSlotProps">
slotProps tidak tersedia di sini
</template>
</current-user>Setiap kali ada beberapa slot, gunakan sintaks berbasis <template> penuh untuk semua slot:
<current-user>
<template v-slot:default="slotProps">
{{ userame }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>Destrukturisasi Properti Slot
Secara internal, slot dengan lingkup bekerja dengan membungkus konten slot Anda dalam fungsi yang menerima satu argumen:
function (slotProps) {
// ... konten slot ...
}Itu berarti nilai v-slot sebenarnya dapat menerima ekspresi JavaScript yang valid yang dapat muncul di posisi argumen definisi fungsi. Jadi di lingkungan yang didukung (komponen file tunggal atau browser modern), Anda juga dapat menggunakan destrukturisasi ES2015 untuk mengambil properti slot tertentu, seperti ini:
<current-user v-slot="{ user }">
{{ userame }}
</current-user>Ini dapat membuat templat lebih bersih, terutama ketika slot menyediakan banyak properti. Ini juga membuka kemungkinan lain, seperti mengganti nama properti, misalnya user menjadi person:
<current-user v-slot="{ user: person }">
{{ personame }}
</current-user>Anda bahkan dapat mendefinisikan cadangan, untuk digunakan jika properti slot tidak terdefinisi:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ userame }}
</current-user>Nama Slot Dinamis
Baru di 2.6.0+
Argumen direktif dinamis juga berfungsi pada v-slot, memungkinkan pendefinisian nama slot dinamis:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>Singkatan Slot Bernama
Baru di 2.6.0+
Sama seperti v-on dan v-bind, v-slot juga memiliki singkatan, menggantikan semua yang sebelum argumen (v-slot:) dengan simbol khusus #. Misalnya, v-slot:header dapat ditulis ulang sebagai #header:
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>Namun, sama seperti direktif lainnya, singkatan hanya tersedia ketika argumen disediakan. Itu berarti sintaks berikut tidak valid:
<!-- Ini akan memicu peringatan -->
<current-user #="{ user }">
{{ userame }}
</current-user>Sebaliknya, Anda harus selalu menentukan nama slot jika ingin menggunakan singkatan:
<current-user #default="{ user }">
{{ userame }}
</current-user>Contoh Lainnya
Properti slot memungkinkan kita mengubah slot menjadi templat yang dapat digunakan kembali yang dapat merender konten berbeda berdasarkan properti masukan. Ini paling berguna ketika Anda merancang komponen yang dapat digunakan kembali yang merangkum logika data sambil memungkinkan komponen induk untuk menyesuaikan bagian dari tata letaknya.
Misalnya, kita mengimplementasikan komponen <todo-list> yang berisi logika tata letak dan pemfilteran untuk sebuah daftar:
<ul>
<li v-for="todo in filteredTodos" v-bind:key="todo">
{{ todo }}
</li>
</ul>Alih-alih mengkodekan konten untuk setiap todo secara keras, kita dapat membiarkan komponen induk mengambil kendali dengan membuat setiap todo sebagai slot, lalu mengikat todo sebagai properti slot:
<ul>
<li v-for="todo in filteredTodos" v-bind:key="todo">
<!-- Kita memiliki slot untuk setiap todo, meneruskan objek `todo` sebagai properti slot. -->
<slot name="todo" v-bind:todo="todo">
<!-- Konten cadangan -->
{{ todo }}
</slot>
</li>
</ul>Sekarang ketika kita menggunakan komponen <todo-list>, kita dapat secara opsional mendefinisikan <template> alternatif untuk item todo, tetapi dengan akses ke data dari anak:
<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todolete">✓</span>
{{ todo }}
</template>
</todo-list>Namun, ini bahkan hampir tidak menyentuh permukaan dari apa yang mampu dilakukan oleh slot dengan lingkup. Untuk contoh penggunaan slot dengan lingkup yang kuat di dunia nyata, kami merekomendasikan untuk menjelajahi perpustakaan seperti Vue Virtual Scroller, Vue Promised, dan Portal Vue.
Sintaks yang Sudah Usang
Direktif v-slot diperkenalkan di Vue 2.6.0, menawarkan API alternatif yang lebih baik untuk atribut slot dan slot-scope yang masih didukung. Alasan lengkap untuk memperkenalkan v-slot dijelaskan dalam RFC ini. Atribut slot dan slot-scope akan terus didukung di semua rilis 2.x mendatang, tetapi secara resmi sudah usang dan pada akhirnya akan dihapus di Vue 3.
Slot Bernama dengan Atribut slot
Sudah usang di 2.6.0+. Lihat di sini untuk sintaks baru yang direkomendasikan.
Untuk meneruskan konten ke slot bernama dari induk, gunakan atribut khusus slot pada <template> (menggunakan komponen <base-layout> yang dijelaskan di sini sebagai contoh):
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>Atau, atribut slot juga dapat digunakan langsung pada elemen biasa:
<base-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</base-layout>Masih ada satu slot tanpa nama, yaitu slot default yang berfungsi sebagai penampung untuk konten yang tidak cocok. Dalam kedua contoh di atas, HTML yang dirender akan menjadi:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>Slot dengan Lingkup dengan Atribut slot-scope
Sudah usang di 2.6.0+. Lihat di sini untuk sintaks baru yang direkomendasikan.
Untuk menerima properti yang diteruskan ke slot, komponen induk dapat menggunakan <template> dengan atribut slot-scope (menggunakan <slot-example> yang dijelaskan di sini sebagai contoh):
<slot-example>
<template slot="default" slot-scope="slotProps">
{{ slotProps }}
</template>
</slot-example>Di sini, slot-scope mendeklarasikan objek properti yang diterima sebagai variabel slotProps, dan membuatnya tersedia di dalam lingkup <template>. Anda dapat menamai slotProps apa saja, mirip dengan memberi nama argumen fungsi di JavaScript.
Di sini slot="default" dapat dihilangkan karena tersirat:
<slot-example>
<template slot-scope="slotProps">
{{ slotProps }}
</template>
</slot-example>Atribut slot-scope juga dapat digunakan langsung pada elemen non-<template> (termasuk komponen):
<slot-example>
<span slot-scope="slotProps">
{{ slotProps }}
</span>
</slot-example>Nilai slot-scope dapat menerima ekspresi JavaScript yang valid yang dapat muncul di posisi argumen definisi fungsi. Ini berarti di lingkungan yang didukung (komponen file tunggal atau browser modern) Anda juga dapat menggunakan destrukturisasi ES2015 dalam ekspresi, seperti ini:
<slot-example>
<span slot-scope="{ msg }">
{{ msg }}
</span>
</slot-example>Menggunakan <todo-list> yang dijelaskan di sini sebagai contoh, berikut adalah penggunaan yang setara dengan slot-scope:
<todo-list v-bind:todos="todos">
<template slot="todo" slot-scope="{ todo }">
<span v-if="todolete">✓</span>
{{ todo }}
</template>
</todo-list>