During development, tables in component libraries such as Element are usually re-wrapped for easy use. This article is made possible by passing in a table that displays multiple levels of table headers between configuration items.

Development environment: VUE3 + Element-Plus (vuE2 also works)

Problem: Table headers and table content areas are customizable, meaning slots are used. Multi-tier slots also involve pass-through, and table column components need to be recursed, so the problem of recursion + slot nesting is mainly solved here

2021.9.10 changes

Problem: The value of slots was obtained from CTX in getInstance, but the values obtained in CTX development and production are inconsistent. See github.com/vuejs/vue-n… Solution: Obtain it from proxy of getInstance, similar to CTX before

A simple introduction

Table has several main functions: table header customization, table content area customization, multi-level table header (effect picture below)

To keep the code concise, features like paging, odd and even, aggregate, and attributes like center, sort, and format are not shown here.

The configuration item received by the table component is a whole object compTableOpts. This object contains some items unrelated to the above main functions, which I will not mention. The main useful item is the columns attribute, which passes in the relevant configuration of all columns.

attribute type instructions
label string The text displayed in the header
prop string The name of the field corresponding to the data (this property is not required if it is a nested header)
headerSlot string If the current header is custom content, the value of this attribute is the name of the slot for the current custom content
slotFlag boolean Whether the current content area is custom content. If true, prop is the name of the slot for the current custom content
children array If the current header is a nested header, the nested region is passed in as the children of the current item

The above description may not be clear, so let’s go straight to the sample code

The actual business page business.vue

<x-table :comp-table-opts="compTableOpts">
    <! Header1 = headerSlot; headerSlot = headerSlot; headerSlot = headerSlot;
    <template #header1>
        <span style="color: red">adult</span>
    </template>
    <template #header2>
	<span style="color: yellow">A minor</span>
    </template>
    <template #header3>
	<span style="color: blue;">hobby</span>
    </template>
    <! The value of minor corresponds to the value of prop defined below, and the slotFlag of this item is true, so the contents of the current slot will be rendered in the table contents.
    <template #minor="row">
	<span>{{ row.rowData.minor === '是' ? '🌼' : '🥬' }}</span>
    </template>
</x-table>

<script>
setup() {
        // Add a few children to show the nested sockets
	 const compTableOpts = reactive({
              columns: [{label: 'ID'.prop: 'id' },
                { label: 'name'.prop: 'name' },
                { label: 'hobby'.prop: 'hobby'.headerSlot: 'header3' },
                {
                  label: 'Base case'.children: [{label: 'gender'.prop: 'sex' },
                    { label: 'age'.prop: 'age' },
                    {
                      label: 'Adult'.children: [{headerSlot: 'header1'.prop: 'adult' },
                        { headerSlot: 'header2'.prop: 'minor'.slotFlag: true}]}]}})}</script>
Copy the code

Table component X-table

The table component needs to be aware of here is the issue of slot+ recursive nesting.

Assume that the specific page is the parent page, the table component is the child page, and the TableColumn component TableColumn is the grandson component. The parent page uses a slot, and the slot places the content, which cannot be obtained in the grandson component.

I can’t find a description of this on github, but the issue of Vue on Github mentions nesting slots

Github.com/vuejs/vue/i…

You suggested using the rendering function, but found it in the process of looking for information

A template solution

The same solution is to get all the slots in the parent page and pass them to the child component with the name of the previous slot. I use a template solution

<el-table
    ref="compoundTableRef"
    v-loading="tableLoading"
    element-loading-text="Loading like hell."
    :data="tableData"
    style="width: 100%;"
   >
      <TableColumn
        v-for="item in compTableOpts.columns"
        key="id"
        :col="item"
      >
        <! $scopedSlots is available for vue2, and the setup value is not needed.
        <template v-for="slot in Object.keys(customSlots)"# [slot] ="scope">
          <! -- Name the slot with the same name and bind the data as before -->
          <slot :name="slot" v-bind="scope" />
        </template>
      </TableColumn>
</el-table>

<script>
import { getCurrentInstance } from 'vue'
setup() {
  const { proxy } = getCurrentInstance()
  constcustomSlots = reactive({ ... proxy.$slots })return {
	 customSlots
   }
}
</script>
Copy the code

TableColumn component TableColumn

This component is a recursive component, so the relevant slots need to be passed as well

<template>
  <el-table-column
    v-if=! "" col.children"
    :label="col.label"
    :prop="col.prop || ''"
    :show-overflow-tooltip=! "" col.els"
  >
    <template #header>
      <slot v-if="col.headerSlot" :name="col.headerSlot" />
      <span v-else>{{ col.label }}</span>
    </template>
    <template #default="scope">
      <slot v-if="col.slotFlag" :name="col.prop" :rowData="scope.row" />
      <span v-else>{{ typeof scope.row[col.prop] ! == 'number' && ! scope.row[col.prop] ? '--' : scope.row[col.prop] }}</span>
    </template>
  </el-table-column>

  <el-table-column
    v-else
    :label="col.label"
  >
    <TableColumn
      v-for="t in col.children"
      :key="t.prop"
      :col="t"
    >
      <! $scopedSlots is available for vue2, and the setup value is not needed.
      <template v-for="slot in Object.keys(customSlots)"# [slot] ="scope">
        <! -- Name the slot with the same name and bind the data as before -->
        <slot :name="slot" v-bind="scope" />
      </template>
    </TableColumn>
  </el-table-column>
</template>

<script>
import { getCurrentInstance, reactive } from 'vue'
export default {
  // Where name is required
  name: 'TableColumn'.props: {
    col: {
      type: Object.default: () = >{}}},setup() {
    const { proxy } = getCurrentInstance()
    constcustomSlots = reactive({ ... proxy.$slots })return {
      customSlots
    }
  }
}
</script>

Copy the code

Over, scatter flowers