一个指令为各大vue组件库的table组件加上动态编辑功能
写在前面
在现代的vue组件库上实现表格编辑其实不难,它们大都提供了作用域插槽,可以在插槽内插入input组件来实现。但这样做有个弊端,太重了!在几十上百行的表格内铺满input卡顿几乎不可避免,而且也很难做到美观。
思考
想实现动态编辑功能其实理论上并不难,我们只需在当前点击的单元格中渲染出input,光标离开销毁这个input即可
实现
这里借助 vue自定义指令来实现
因为指令不支持直接数据双向绑定,参数上设计了value和input2个钩子,一个用来取值,一个用来赋值
import Vue from 'vue';
const getTargetEl = (el, nodeName = 'TD') =>
el.nodeName === nodeName ? el :
el.parentElement ? getTargetEl(el.parentElement, nodeName) : undefined;
function createInput(options) {
const input = document.createElement('input');
input.className = options.inputClass || 'td-input';
input.type = options.inputType || 'text';
input.value = typeof options.value === 'function' ? options.value() : options.value;
return input;
}
const targets = [];
function handle(el, binding) {
const options = binding.value || {};
const targetEl = getTargetEl(el, options.nodeName || 'TD');
if (targetEl) {
let target = targets.find(v => v.el === targetEl);
if (!target) {
target = {options, el: targetEl};
targets.push(target);
let inputEl;
targetEl.style.position = 'relative';
targetEl.addEventListener(options.type || 'click', () => {
if (!inputEl) {
inputEl = createInput(target.options);
targetEl.appendChild(inputEl);
inputEl.focus();
inputEl.onblur = () => {
target.options.input && target.options.input(inputEl.value);
targetEl.removeChild(inputEl);
inputEl = undefined;
};
}
});
} else {
target.options = options;
}
}
}
const TdInput = {
inserted: handle,
update: handle,
};
/**
* v-td-input="{value: () => row.name, input: v => row.name = v }"
*/
Vue.directive('td-input', TdInput);代码写完了,我们需要给这个input定义样式,我在createInput的代码中默认给它加了.td-input这个类,并提供了inputClass这个配置项方便自定义扩展
<style>
.td-input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 0 10px;
box-shadow: 1px 1px 20px rgba(0,0,0,.15);
box-sizing: border-box;
background-color: #FFF;
background-image: none;
border: 1px solid #DCDFE6;
display: inline-block;
outline: 0;
}
.td-input:focus {
border-color: #5FB878!important
}
</style>使用
在作用域插槽内通过一个可以绑定指令的dom来使用
v-td-input="{value: () => row.date, input: v => row.date = v }"类似下面这个例子
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
<template slot-scope="{row}">
<span v-td-input="{value: () => row.date, input: v => row.date = v }">{{ row.date }}<span>
</template>
</el-table-column>
</el-table>提供了一个type配置项,指定触发事件,默认是单击click,你也可以像下面这样设置成dblclick来实现双击编辑
<span
v-td-input="{
type: 'dblclick',
value: () => row.date,
input: v => row.date = v
}"
>
{{ row.date }}
<span>另外有一个配置项nodeName用来筛选绑定事件对象,默认是TD,如果想在表头使用,需改成TH,参考下面element-ui的例子
效果
我测试了比较热门的几个vue组件库,都能正确工作
element-ui
iview
antd-vue
原文:https://segmentfault.com/a/1190000021871839
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!