前面講完表單這樣常見又複雜的製作方式,其實在對付的是一種「以物件為根的」資料結構。並且同時又處理掉了物件的物件,以及物件的陣列。那麼所有的表單其實就沒有什麼好害怕的了,是吧?(是嗎?)
那麼,如果以要處理「以陣列為根的」資料結構呢?通常,我們會使用 UI component 來處理這個問題。哈哈~~
這一次,我就以 BootstrapVue 為例。(這一招可以適用於其它 UI component 唷!)
來看看我們會遇到什麼難題吧!
以 Table | Components | BootstrapVue 這個元件為主角
要處理一個這樣的資料
[
{ age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ age: 38, first_name: 'Jami', last_name: 'Carney' }
]
[
{
key: 'first_name',
label: '名',
},
{
key: 'last_name',
label: '姓',
},
{
key: 'age',
label: '年紀',
},
]
設定到表格 component 時,其實就是放進兩個
<b-table striped hover :items="items" :fields="fields"></b-table>
畫面像這樣
這樣的需求,是不是能夠做一個 list 的 component 了呢?
src/components/list.vue
<div class="list">
<b-table striped hover :items="items" :fields="fields"></b-table>
</div>
export default {
name: 'List',
props: {
items: {
type: Array,
required: true
},
fields: {
type: Array,
required: true
}
}
}
src/views/Users.vue
<List :items="$store.getters.users" :fields="fields"></List>
src/views/Departments.vue
<List :items="$store.getters.departments" :fields="fields"></List>
做完了。謝謝 還沒完!
咦?我們不是做到這兩點了嗎?
但是,如果我們想要客製欄位,怎辦?
Slots - Table | Components | BootstrapVue
在我們重新封裝了 UI Component 的元件之後,是不是就受限了使用方式了呢?
還可以依照官網的說明使用我們封裝過的元件嗎?
這樣一來,就可以封裝「共用特定的使用方式」,依官網的使用方式,又可以依不同的資料客製使用方式。
聽不太懂?!沒關係,先看結果,究竟是要做到什麼效果呢?
在 Custom data rendering 這一段有介紹怎麼樣寫。而最終就是想要照著寫。
記住,任何自己發明的 props、slot 最好都要寫一份文件,不要以為別人都看得懂你的設計。QQ
因為我不想寫文件,所以致力於「別人的文件,就是我的文件」
只要用法和 UI Component 一樣,基本上就可以使用它強大的功能與文件。
<List :items="$store.getters.users" :fields="fields">
<template #cell(name)="{ item }">
{{ item.last_name }} {{ item.last_name }}
</template>
</List>
[
{
key: 'name',
label: '姓名',
},
{
key: 'age',
label: '年紀',
},
]
透過前面介紹的技巧知道 $attrs 和 $listeners 可以在自訂的 component 上使用 UI Component 。但是,<slot>
呢?
<div class="list">
<b-table striped hover :items="items" :fields="fields">
<!-- 在這裡究竟要放什麼呢? -->
</b-table>
</div>
探索之前,先印出來看
export default {
name: 'List',
mounted() {
console.log('在這裡印出要測試的 API')
}
}
$scopedSlot
<div class="list">
<pre>{{ Object.keys($scopedSlot) }}</pre>
</div>
export default {
name: 'List',
mounted() {
console.log(this.$scopedSlot)
}
}
$slots
<div class="list">
<pre>{{ Object.keys($slots) }}</pre>
</div>
export default {
name: 'List',
mounted() {
console.log(this.$scopedSlot)
}
}
總之,我們得了一個長得像這樣的東西
[ "cell(name)" ]
由於因為 BootstrapVue 還不支援 Vue3 所以剩下部份,我們就用 Vue2 實現吧! (目前看起來就 API 不太一樣而已)
<b-table :items="items" :fields="fields">
<template v-for="(value, key) in $scopedSlots" v-slot:[key]="{ item }">
<slot :name="key" v-bind:item="item"></slot>
</template>
</b-table>
這樣一來,原本我們寫成這樣的東西
<b-table striped hover :items="$store.getters.users" :fields="fields">
<template #cell(name)="{ item }">
{{ item.last_name }} {{ item.last_name }}
</template>
</b-table>
就可以寫成下面這兩個組合
src/views/users.vue
<List :items="$store.getters.users" :fields="fields">
<template #cell(name)="{ item }">
{{ item.last_name }} {{ item.last_name }}
</template>
</List>
src/components/list.vue
<b-table striped hover :items="items" :fields="fields">
<template v-for="(value, key) in $scopedSlots" v-slot:[key]="{ item }">
<slot :name="key" v-bind:item="item"></slot>
</template>
</b-table>
成功的將 bootstrap-vue 的 table,其中的 slot 移植到我們自己的 component 上使用。這樣一來,還有什麼可以阻止得了我呀!哈哈哈
之後,不管是 props, events 甚至是 slot 都可以任意使用啦,任何的 component 我就可以自由的封裝共用的設定,而開放非共用的設定。包裝成自己想要的 component 了!
我們實作過下面這四種變化了
未來遇到任何奇奇怪怪的資料結構,就完全不用害怕囉!
不用再說「呃...後端這個結構太複雜,是不是可以拆開做呢?」
最後,你覺得這一系列有進階嗎?
歡迎留言告訴我。
也歡迎你留下自己習慣的寫法 (repo),與我分享唷!