本文介绍如何利用Vue.js自定义指令重构传统的全选逻辑。通过封装v-check-all指令,实现全选状态与列表数据的自动同步,解决原生computed方案复用性差的问题。文中详细解析了指令的bind与update钩子用法,以及$watch监听数组变化的实现细节,并提供完整代码示例。
Vue.js自定义指令实现列表全选功能教程
由于初期对Vue.js掌握尚浅,全选功能的实现思路主要借鉴了社区中的常见做法。
第一步,从服务器获取数据后,为列表中的每个项目添加checked属性。
第二步,计算已选中项目的数量selectCount。当该数量与列表总项目数selectItems相等时,触发全选状态selectAll为选中。
第三步,点击全选框时,将所有项目的checked属性设为true;取消全选时,则设为false。
第四步,每当selectItems中的属性发生变化,系统会将所有checked为true的项目收集到数组checkedGroups中。
以下是基于上述逻辑的实现代码:
//全选
data: function() {
return {
selectItems: [], // 从服务器拿到的数据
}
},
computed: {
// 全选checkbox绑定的model
selectAll: {
get: function() {
return this.selectCount == this.selectItems.length;
},
set: function(value) {
this.selectItems.forEach(function(item) {
item.checked = value;
});
return value;
}
},
//选中的数量
selectCount: {
get: function() {
var i = 0;
this.selectItems.forEach(function(item) {
if (item.checked) {
i++;
}
});
return i;
}
},
//选中的数组
checkedGroups: {
get: function() {
var checkedGroups = [];
this.selectItems.forEach(function(item) {
if (item.checked) {
checkedGroups.push(item);
}
});
return checkedGroups;
}
}
}
这种传统方式存在明显缺陷:复用性极低,每次使用都需重新编写computed逻辑;此外,selectAll、checkedGroups和selectItems等变量名固定,缺乏灵活性。
因此,在本项目中,我尝试通过Vue.js的自定义指令重新实现全选功能。其核心思路与computed类似,具体代码见下:
export default {
'check-all': {
twoWay: true,
params: ['checkData'],
bind() {
/**
- 如果所有的列表的checked属性都为true,则选中全选框,否则不选中全选框
*/
this.vm.$watch(this.params.checkData, (checkData) => {
if (checkData.every((item) => item.checked)) {
this.set(true);
} else {
this.set(false);
}
}, { deep: true });
},
// checkAll发生更改时
update(checkAll) {
/**
- 如果全选框被选中,则将列表的所有checked属性转为true,否则转为false
*/
if (checkAll) {
this.vm[this.params.checkData].forEach((item) => {
item.checked = true;
});
} else {
this.vm[this.params.checkData].forEach((item) => {
item.checked = false;
});
}
},
},
};
调用方式如下:
<input type="checkbox" v-model="checkAll" v-check-all="checkAll" check-data="checkData">
<ul>
<li v-for="item in checkData">
<input type="checkbox" v-model="item.checked">
{{item.text}}
</li>
</ul>
这种新用法的主要优势在于:
首先,使用便捷,只需在目标元素上添加v-check-all指令并绑定check-data即可。
其次,全选的model名称和数组名均可自定义。例如,全选model可不叫checkAll而叫checkAllData,列表数组也可不叫checkData而叫dataFromServer。
在指令定义中,设置twoWay为true,即可通过this.set(value)来更新checkAll的值。同时,使用params接收绑定在指令元素上的属性值checkData,即需要操作的目标数组。
利用this.vm获取当前指令的上下文,并调用该上下文的$watch方法来监听checkData的变化。若checkData中所有项目均被选中,则将checkAll设为true;否则设为false。
当指令绑定的值(即checkAll)发生变化时,若为true,则将checkData中所有项目的checked属性设为true;若为false,则设为false。至此,一个完整的全选指令逻辑即告完成。
在开发此指令时,曾尝试使用paramWatchers来监听checkData的变化,但发现checkData变动时并未触发paramWatchers的回调。查阅源码后得知,paramWatchers底层同样调用$watch,但不支持深度检测。
Directive.prototype._setupParamWatcher = function (key, expression) {
var self = this;
var called = false;
var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
self.params[key] = val;
// since we are in immediate mode,
// only call the param change callbacks if this is not the first update.
if (called) {
var cb = self.paramWatchers && self.paramWatchers[key];
if (cb) {
cb.call(self, val, oldVal);
}
} else {
called = true;
}
}, {
immediate: true,
user: false
});(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch);
};
总结
以上即为本文的全部内容。如有不足之处,恳请指正。希望本文内容能对大家有所帮助。