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