前言
本文的研究思路是通过阅读Element源码,然后自动动手一步一步编写组件,完善其对应功能。
相信量变引起质变,我们继续来研究Element的组件。
以前的研究:
Element组件源码研究-Layout,Link,Radio
还是老套路,先实现最简单的组件封装,再不断给其添加功能。
最基本的实现
区别于原生checkbox只有一个方形选择框,element的checkbox有内容区域。
<el-checkbox v-model="checked">内容区域</el-checkbox>
且通过v-model来绑定是否选中。
下面贴出基本实现代码:
<template>
<label
class="el-checkbox"
:class="[
{ 'is-checked': isChecked }
]"
>
<span class="el-checkbox__input"
:class="{
'is-checked': isChecked,
}"
>
<span class="el-checkbox__inner"></span>
<input
ref="checkbox"
class="el-checkbox__original"
type="checkbox"
:checked="isChecked"
[@input](/user/input)="handleInput"
@change="handleChange"
>
</span>
<span class="el-checkbox__label" v-if="$slots.default">
<slot></slot>
</span>
</label>
</template>
<script>
export default {
name: 'ElCheckbox',
props: {
value: {},
},
computed: {
isChecked() {
return this.value
}
},
methods: {
handleChange(ev) {
this.$emit('change', ev.target.checked, ev);
},
handleInput(ev) {
this.$emit('input', ev.target.checked, ev);
}
},
}
</script>
之前我一直以为v-model就是:value和@input的结合体。原来在原生checkbox上,是:check和@input的结合体。所以上面也可以通过v-model实现对原生checkbox的绑定:
<!-- :checked="isChecked"
[@input](/user/input)="$emit('input', $event.target.checked , ev)" -->
// 将上面的代码替换为v-model="model",model是一个计算属性
<input
ref="checkbox"
class="el-checkbox__original"
type="checkbox"
v-model="model"
@change="handleChange"
>
...
computed: {
model: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
}
},
isChecked() {
return this.model
}
},
这样,我们的实现方式就贴近于源码的实现方式了。
禁用状态
先写测试代码
<el-checkbox v-model="checked" disabled>内容区域</el-checkbox>
再为组件添加disabled,根据属性判断样式,给input标签添加:disabled="disabled"即可。
效果如下:
多选框组
测试代码:
<el-checkbox-group v-model="checkList" @change="onChange">
<el-checkbox label="复选框 A"></el-checkbox>
<el-checkbox label="复选框 B"></el-checkbox>
<el-checkbox label="复选框 C"></el-checkbox>
<el-checkbox label="禁用" disabled></el-checkbox>
<el-checkbox label="选中且禁用" disabled></el-checkbox>
</el-checkbox-group>
多选框组的工作方式和单个Checkbox有区别,值不是true/false了,而是绑定在group组件上的一个数组,选中的checkbox的label会被添加到此数组中。
写一个CheckboxGroup组件。实现思路和RadioGroup非常类似。
<template>
<div class="el-checkbox-group" role="group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElCheckboxGroup',
componentName: 'ElCheckboxGroup',
props: {
value: {},
disabled: Boolean,
},
};
</script>
CheckboxGroup组件只是一个容器,主要的实现逻辑还在Checkbox组件中。
多选框组工作方式的不同,我们也可以猜到Checkbox组件中的逻辑实现也不一样。代码大体如下:
model: {
get() {
return this.isGroup() ? this.store : this.value
},
set(val) {
if (this.isGroup()) {
this.dispatch('ElCheckboxGroup', 'input', [val]);
} else {
this.$emit('input', val);
}
}
},
...
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElCheckboxGroup') {
parent = parent.$parent;
} else {
this._checkboxGroup = parent;
return true;
}
}
return false;
},
store() {
return this._checkboxGroup ? this._checkboxGroup.value : this.value;
},
上面的代码实现了在CheckBox中操作父Group的:value的值。
通过isGroup方法判断是否在group中,如果在,就走另一套逻辑。这时候model的set方法的参数val不是true/false。而是checkList:[‘复选框 A’,‘选中且禁用’]这样的数组了。绑定组件的value值彻底变化了。在原生组件上添加:value=“label”,这里面利用了一个知识点,当value有值时(label有值,也就是在group中),原生组件的v-model绑定的不再是checked属性,而是value属性了。
此时再判断当前CheckBox有没有被选中:
isChecked() {
if ({}.toString.call(this.model) === '[object Boolean]') {
return this.model;
} else if (Array.isArray(this.model)) {
// 重点是这句,如果model是数组,说明组件在group中,判断lable在不在group的value中
return this.model.indexOf(this.label) > -1;
}
return false;
},
效果如下:
indeterminate 状态
indeterminate 属性用以表示 checkbox 的不确定状态,一般用于实现全选的效果。
测试代码如下:
<el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange">
<el-checkbox v-for="city in cities" :label="city" :key="city">{{city}}</el-checkbox>
</el-checkbox-group>
const cityOptions = ['上海', '北京', '广州', '深圳'];
data() {
return {
checkAll: false,
checkedCities: ['上海', '北京'],
cities: cityOptions,
isIndeterminate: true
};
},
methods: {
handleCheckAllChange(val) {
this.checkedCities = val ? cityOptions : [];
this.isIndeterminate = false;
},
handleCheckedCitiesChange(value) {
let checkedCount = value.length;
this.checkAll = checkedCount === this.cities.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length;
}
},
组件中添加indeterminate属性,控制样式即可,没有啥复杂度,效果如下:
可选项目数量的限制
-
组件添加isLimitExceeded:false
-
添加isLimitDisabled计算属性。根据group的max和min属性判断自己还能不能被选择。
isLimitDisabled() {
const { max, min } = this._checkboxGroup;
return !!(max || min) &&
(this.model.length >= max && !this.isChecked) ||
(this.model.length <= min && this.isChecked);
},
- 最后在isDisabled计算属性中添加this.isLimitDisabled的禁用逻辑。
效果如下:
按钮样式
新写一个组件CheckboxButton,逻辑与Checkbox基本类似,不再赘述。详细代码可以下下面的码云仓库。
带有边框
给组件添加属性,增加样式判断。不再赘述。
效果如下:
总结
Checkbox组件的研究就到这里。代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/checkbox/
其他组件源码研究:
Element组件源码研究-Layout,Link,Radio
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 猎户座小陈 原文链接:https://juejin.im/post/6855928401962729486