解读element-ui中table组件部分源码与需求分析
- 2022-05-05 10:00:00
- 彭小呆
- 转贴:
- 掘金
- 7254
一、前言
如果说Vue、React、Angular是前端三剑客,那么element-ui可以说在中后台领域占据半壁江山,github star数 43k之多。至今,它拥有了84个组件(Version 2.13.0)。
二、起因
<el-tableref="multipleTable":data="tableData"tooltip-effect="dark"style="width: 100%"@selection-change="handleSelectionChange"><el-table-columntype="selection"width="55"></el-table-column></el-table>
配合 selection-change事件,可以获得用户选中的row组成的数组。
分页表格应该像下面这样:
selection.forEach(row => {this.$refs.multipleTable.toggleRowSelection(row);});
this.$nextTick(() => {selection.forEach(row => {this.$refs.multipleTable.toggleRowSelection(row);});});
玩个串串……
三、源码分析
3.1 结构
// index.jsimport ElTable from './src/table';/* istanbul ignore next */ElTable.install = function(Vue) {Vue.component(ElTable.name, ElTable);};
export default ElTable;
然后src里面包含一个store文件夹和一些table的组件:body、column、footer、header、layout等,工具类文件util.js,配置文件config.js,and 一个dropdown(没懂)、一个layout-observer(从名字上看是监听layout的)、filter-panel(过滤用的)大概就这样。
3.2 找到它
toggleRowSelection(row, selected, emitChange = true) {const changed = toggleRowStatus(this.states.selection, row, selected);if (changed) {const newSelection = (this.states.selection || []).slice();// 调用 API 修改选中值,不触发 select 事件if (emitChange) {this.table.$emit('select', newSelection, row);}this.table.$emit('selection-change', newSelection);}}
打开util.js文件,顺利的找到了以下代码:
export function toggleRowStatus(statusArr, row, newVal) {let changed = false;const index = statusArr.indexOf(row);const included = index !== -1;const addRow = () => {statusArr.push(row);changed = true;};const removeRow = () => {statusArr.splice(index, 1);changed = true;};if (typeof newVal === 'boolean') {if (newVal && !included) {addRow();} else if (!newVal && included) {removeRow();}} else {if (included) {removeRow();} else {addRow();}}return changed;}
- 坑1:如果这个元素是一个对象(Object),大家应该知道对象是引用类型,也就是说用 indexOf去判断,只能判断出对象引用的地址是不是一样的,并不能判断里面的值是不是一样的。
// config.js 29行// 这些选项不应该被覆盖export const cellForced = {selection: {renderHeader: function(h, { store }) {return <el-checkboxdisabled={ store.states.data && store.states.data.length === 0 }indeterminate={ store.states.selection.length > 0 && !this.isAllSelected }nativeOn-click={ this.toggleAllSelection }value={ this.isAllSelected } />;},renderCell: function(h, { row, column, store, $index }) {return <el-checkboxnativeOn-click={ (event) => event.stopPropagation() }value={ store.isSelected(row) }disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false }on-input={ () => { store.commit('rowSelectedChanged', row); } } />;},sortable: false,resizable: false}// ...省略}
只贴出有用的,从大的耳朵看,导出了一个模块叫 cellForced,虽然我不知道什么意思。(四级没过,砸砸辉)。
// config.js 29行// 这些选项不应该被覆盖export const cellForced = {selection: {renderHeader: function(h, { store }) {return <el-checkboxdisabled={ store.states.data && store.states.data.length === 0 }indeterminate={ store.states.selection.length > 0 && !this.isAllSelected }nativeOn-click={ this.toggleAllSelection }value={ this.isAllSelected } />;},renderCell: function(h, { row, column, store, $index }) {return <el-checkboxnativeOn-click={ (event) => event.stopPropagation() }value={ store.isSelected(row) }disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false }on-input={ () => { store.commit('rowSelectedChanged', row); } } />;},sortable: false,resizable: false}// ...省略}
value={ store.isSelected(row) }
isSelected(row) {const { selection = [] } = this.states;return selection.indexOf(row) > -1;},
- 坑2: 又是用的indexOf判断一个对象是否在数组中。
虽然在数据结构和内容上,这两个row都一样,假设都是以下的玩意:
row1和row2他喵的不相等。
四、解决方案
- 等element-ui更新,有人解决,提issue。
- 将保存在变量中的row和当前得到的table的data中的row进行深度比对,得到选中的row在当前tableData中的位置,然后将使用toggleRowSelection(tableData[index])的形式,确保你二舅是你二舅。
- 自行封装多选表格组件,不用自带的selection,而是通过自己实现这个功能(可以参考评论区 shaonialife童鞋的方法,非常棒)。
- 改写Array.prototype.indexOf方法,使内部判断逻辑在对象的时候进行深度比对。
- 通过设置el-table中的row-key为id,然后设置type="selection"的那一列的reserve-selection为true,这样在你切换页码的时候,之前的页码选中的值会被保留(由评论区的 晗__ 小伙伴提出,感谢)。但是还是要注意一点,如果需要默认勾选,依旧要判断默认勾选的那几个row在数据源tableData中的位置,然后通过toggleRowSelection设置进去。
<el-table-column:align="tableColumn.align"type="index"label="序号"width="70"><template slot-scope="scope"><el-checkbox :value="!!checkedRowIds[scope.row.id]" @change="(val) => { toggleRowSelection(val, scope.row) }"/></template></el-table-column>
const checkedRowIds = {0: true,1: true,2: false}
toggleRowSelection(val, row) {const { checkedRowIds } = this.$propsconst { id } = rowthis.$set(checkedRowIds, id, val)const includes = checkedRowIds.hasOwnProperty(id)const remove = () => delete checkedRowIds[id]if (!val && includes) remove()const keys = Object.keys(checkedRowIds)const arrToString = arr => arr.join(',')const ids = arrToString(keys)// ids: 1,2,3},
// 利用row-key与reserve-selection的组合拳<el-tablev-loading="loading":data="tableData"row-key="id"@selection-change="rowChange"><el-table-column:reserve-selection="true"type="selection"width="55"/></el-table>
- 联系人:阿道
- 联系方式: 17762006160
- 地址:青岛市黄岛区长江西路118号青铁广场18楼