表格元件共用攻略

前面讲完表单这样常见又复杂的制作方式,其实在对付的是一种「以物件为根的」资料结构。并且同时又处理掉了物件的物件,以及物件的阵列。那麽所有的表单其实就没有什麽好害怕的了,是吧?(是吗?)

那麽,如果以要处理「以阵列为根的」资料结构呢?通常,我们会使用 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>
  • striped
  • hover

画面像这样

需求

  • 如果整个站,不只是 User 这个 Array 呢?共用的只有 Array 操作,而元素内的型别需要各别处理。
  • 想要让整个网站的 table 吃相同的设定、想改时,可以一起改

这样的需求,是不是能够做一个 list 的 component 了呢?

共用表格 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>

做完了。谢谢 还没完!

咦?我们不是做到这两点了吗?

  • 如果整个站,不只是 User 这个 Array 呢?共用的只有 Array 操作,而元素内的型别需要各别处理。
  • 想要让整个网站的 table 吃相同的设定、想改时,可以一起改

但是,如果我们想要客制栏位,怎办?

Slots - Table | Components | BootstrapVue

问题是什麽?

在我们重新封装了 UI Component 的元件之後,是不是就受限了使用方式了呢?
还可以依照官网的说明使用我们封装过的元件吗?

这样一来,就可以封装「共用特定的使用方式」,依官网的使用方式,又可以依不同的资料客制使用方式。

听不太懂?!没关系,先看结果,究竟是要做到什麽效果呢?

技术需求

  1. 封装对於 table 的 props 设定。
  2. 客制栏位渲染方式,使用的是 slot ,要在自己封装好的元件,使用 bootstrap 的 slot

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> 呢?

找适合的 API

<div class="list">
  <b-table striped hover :items="items" :fields="fields">
    <!-- 在这里究竟要放什麽呢? -->
  </b-table>
</div>

探索之前,先印出来看

export default {
  name: 'List',
  mounted() {
    console.log('在这里印出要测试的 API')
  }
}

如果,你用的是 Vue2 可以试试 $scopedSlot

<div class="list">
  <pre>{{ Object.keys($scopedSlot) }}</pre>
</div>

export default {
  name: 'List',
  mounted() {
    console.log(this.$scopedSlot)
  }
}

如果,你用的是 Vue3 可以试试 $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 了!

最後一个资料驳动的拼图: 表格 = 是 Array 里的 Object

我们实作过下面这四种变化了

  • Object
  • Object 里的 Object
  • Object 里的 Array
  • Array 里的 Object

未来遇到任何奇奇怪怪的资料结构,就完全不用害怕罗!
不用再说「呃...後端这个结构太复杂,是不是可以拆开做呢?」

最後,你觉得这一系列有进阶吗?
欢迎留言告诉我。

也欢迎你留下自己习惯的写法 (repo),与我分享唷!


<<:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 30 - 学习cypress intercept 与後记

>>:  IT铁人DAY 29-Template Method 模板模式

Day10 分页与分段的记忆体管理

前言 前几天讲完了行程管理的部分,其中有个部分讲到,所谓的ready 或者说 task_runnin...

Day 07-Terraform 写起来不够 DRY 的问题,这解 Terragrunt 你试试看

课程内容与代码会放在 Github 上: https://github.com/chechiacha...

【Side Project】 目标:网路订餐网站

作为软件工程师的我们应该都曾有过一个疑问,开始写程序之前,要不要先做规划。 这个问题也曾经困扰着我跟...

Day-12 RelativeLayout

RelativeLayout(相对布局) 在RelativeLayout中,元件的位置可以是相对於整...

[资料库] 学习笔记 - 商城交易之订单付款与付款後产生送货单

这次练习的题目是做出商城中订单付款与付款後产生送货单的功能 功能主要需求:记录是否付款、付款方式是什...