拆解checkbox的v-model

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 拆解checkbox的v-model

拆解checkbox的v-model


项目中,原始使用 checkbox 的话,一般绑定 v-model 就行,但是,如果想实现类似微信从通讯录选中好友来建群的效果,如下图,加上可以组里面再选择,就需要将 v-model 拆解成 checked 和 change 事件,然后封装单项组件。

v-model 可以绑定两种类型的值,BooleanArray。所以组件封装之后可以这样使用:

//- item是内容区的相关信息,比如头像 昵称 uid
//- 1 boolean类型的时候,通常用于全选 组选的情况
checkboxEnhanced(v-model='bool') 同意协议
//- 2 array类型的时候,这时候可以循环
checkboxEnhanced(v-for="(item,index) in list" v-model='arr'  :value='item.code') {{item.name}}



项目的github

v-model 是 boolean 的时候

组件逻辑

  • 将原始的input隐藏,通过点击内容区触发input的click事件,从而达到控制其change事件
  • change事件会改变元素的checked属性,再向父组件派发事件,父组件根据传递的值更新自己的checked,而更新后的值又会再影响这个组件的checked,形成闭环。
  • 这里为了方便父组件使用v-model,将model设置为change事件和checkedvue官网文档
<!-- checkboxBoolean.vue -->
<template lang="pug">
div.checkbox-box
  div.checkbox-content-box(@click='$refs.input.click()')
    //- 自定义选中图标
    div.icon-box
      img.icon(alt='' :src='checked?"https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/icon_selected.png":"https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/icon_not_selected.png"')
    div.content
      //- slot可以自定义后面的内容
      slot
        //- 这里可以根据自己的项目灵活改变
        div.avatar-box
          img.img-avatar(alt='' :src='item.avatar || " https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/default_avatar.png"')
        .name {{item.name}}
  //- 原始input隐藏,这里的value看情况使用,可以不传,change是将选择事件抛出去,让父组件知晓
  input(ref='input' hidden type='checkbox' :checked='checked' :value='value' @change='changeInput($event)')
</template>
<script>
/**
 使用方法
  checkbox-boolean(v-model='checked') 嘿哈
 加上value和change事件也是没问题的
    checkbox-boolean(v-model='checked' :item='item' :value='item.code' @change='handleChange')
 */
export default {
  name: "checkbox-boolean",
  model: {
    prop: "checked",
    event: "change",
  },
  props: {
    item: {
      type: Object,
      default() {
        return {};
      },
    },
    checked: {
      type: Boolean,
      required: true,
      default() {
        return false;
      },
    },
    value: {
      // type: [Boolean, String, Number],
      default() {
        return this.item;
      },
    },
  },
  methods: {
    changeInput($event) {
      // 第一项就将是否选中扔出去,这里注意,扔出去之后,父组件用v-model的话,父组件的值会自动变化
      // v-model是个语法糖,本质上相当于父组件 checkbox-item(:checked='checked' @change='checked=$event')
      this.$emit("change", $event.target.checked, $event);
    },
  },
};
</script>
<style scoped>
.checkbox-box {
  display: flex;
  margin-left: 30px;
  margin-right: 30px;
}
.checkbox-content-box {
  display: flex;
  height: 80px;
  line-height: 80px;
  margin-top: 12px;
  margin-bottom: 12px;
  width: 100%;
}
.icon {
  width: 34px;
  height: 34px;
  display: inline-block;
  vertical-align: middle;
}
.content {
  display: flex;
  margin-left: 20px;
}
.img-avatar {
  display: block;
  width: 80px;
  height: 80px;
  border-radius: 50%;
}
.name {
  margin-left: 15px;
  color: #333;
  font-size: 30px;
}
</style>

v-model 是 Array 的时候

组件实现逻辑:

  • 当父组件传过来的checked是数组类型的时候,
  • 当前组件的初始状态curChecked是,看数组里有没有当前value,有就是选中,没有就不选中
  • 数组是引用类型,为了不改变父组件的值,这里使用selectedList复制一份checked
  • 当checkbox有change事件的时候,选中就将value值push到selectedList,否则删掉,然后将selectedList抛给父组件
  • !!! 注意这里使用computed,以为别的复选框选择的时候,selectedList也会跟着变化
<!-- checkboxArray.vue -->
<template lang="pug">
div.checkbox-box
  div.checkbox-content-box(@click='$refs.input.click()')
    //- 自定义选中图标
    div.icon-box
      img.icon(alt='' :src='curChecked?"https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/icon_selected.png":"https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/icon_not_selected.png"')
    div.content
      //- slot可以自定义后面的内容
      slot
        //- 这里可以灵活改变
        div.avatar-box
          img.img-avatar(alt='' :src='item.avatar || " https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/default_avatar.png"')
        .name {{item.name}}
  //- 原始input隐藏,这里的value看情况使用,可以不传,change是将选择事件抛出去,让父组件知晓
  input(ref='input' hidden type='checkbox' :checked='curChecked' :value='value' @change='changeInput($event)')
</template>
<script>
export default {
  name: "checkbox-array",
  model: {
    prop: "checked",
    event: "change",
  },
  props: {
    item: {
      type: Object,
      default() {
        return {};
      },
    },
    checked: {
      required: true,
      default() {
        return [];
      },
    },
    value: {
      default() {
        return this.item;
      },
    },
  },
  computed: {
    selectedList: {
      get() {
        return [...this.checked];
      },
      set() {
        console.log();
      },
    },
    curChecked() {
      return this.checked.includes(this.value);
    },
  },
  methods: {
    changeInput($event) {
      let { checked } = $event.target;
      // 看下选中的值在数组中的索引,这里不用原生的value,因为$event.target.value始终是字符串类型
      let index = this.selectedList.indexOf(this.value);
      // 选择的时候,selectedList跟着变化
      checked
        ? this.selectedList.push(this.value)
        : index !== -1 && this.selectedList.splice(index, 1);
      this.$emit("change", this.selectedList, $event);
    },
  },
};
</script>
<style scoped>
.checkbox-box {
  display: flex;
  margin-left: 30px;
  margin-right: 30px;
}
.checkbox-content-box {
  display: flex;
  height: 80px;
  line-height: 80px;
  margin-top: 12px;
  margin-bottom: 12px;
  width: 100%;
}
.icon {
  width: 34px;
  height: 34px;
  display: inline-block;
  vertical-align: middle;
}
.content {
  display: flex;
  margin-left: 20px;
}
.img-avatar {
  display: block;
  width: 80px;
  height: 80px;
  border-radius: 50%;
}
.name {
  margin-left: 15px;
  color: #333;
  font-size: 30px;
}
</style>

将两种情况合并

这里简单使用 component 组件,别忘了slot。vue 官网介绍component

<!--CheckboxEnhanced.vue-->
<template lang="pug">
div
  component(v-if="!isArray" is='CheckboxBoolean' v-bind='$attrs' v-on='$listeners')
    //- 自定义后面的内容
    slot
  component(v-else is='CheckboxArray' v-bind='$attrs' v-on='$listeners')
    slot
</template>
<script>
import CheckboxBoolean from "@/components/CheckboxBoolean";
import CheckboxArray from "@/components/CheckboxArray";
export default {
  name: "checkbox-enhanced",
  components: {
    CheckboxArray,
    CheckboxBoolean,
  },
  model: {
    prop: "checked",
    event: "change",
  },
  created() {
    this.isArray = Array.isArray(this.$attrs.checked);
  },
  data() {
    return {
      isArray: false,
    };
  },
};
</script>

综合使用

//- Home.vue
<template lang="pug">
div
  h2 boolean
  div
    div {{checked}}
    checkbox-enhanced(v-model='checked' @change='change') 同意协议
  h2 array
  div
    div {{selectedCodes}}
    checkbox-enhanced(v-for='(item,index) in items' :key='index' v-model='selectedCodes' :item='item' :value='item.code' @change='changeItem')
  h2 array 自定义传入的内容
  div
    div {{selectedCodes}}
    checkbox-enhanced(v-for='(item,index) in items' :key='index' v-model='selectedCodes' :value='item.code' @change='changeItem') {{item.name}}
  h3 group
    section(v-for='(group,index) in groups' :key='index')
      br
      div {{group.name}}
      checkbox-enhanced(v-for='(item,idx) in group.children' :key='idx' v-model='selectedCodes'  :item='item' :value='item.code'  @change='changeItem')
</template>
<script>
// @ is an alias to /src
import CheckboxEnhanced from "@/components/CheckboxEnhanced.vue";
export default {
  name: "Home",
  components: {
    CheckboxEnhanced
  },
  data() {
    return {
      item: { code: 6, name: "组" },
      items: [
        { code: 1, name: "huahua" },
        { code: 2, name: "huahua2" }
      ],
      selectedCodes: [],
      checked: true,
      groups: [
        {
          name: "a组的成员",
          children: [{ code: 3, name: "huahua3" }]
        },
        {
          name: "b组的成员",
          children: [{ code: 4, name: "huahua4" }]
        }
      ]
    };
  },
  watch: {
    groups(newVal) {
      console.log(newVal);
    }
  },
  methods: {
    changeItem() {
      console.log(arguments);
    },
    change(group) {
      console.log(group);
    }
  }
};
</script>


相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
8月前
组件v-model
组件v-model
67 0
|
索引
Layui 内置方法 - layer.prompt_(输入层)
Layui 内置方法 - layer.prompt_(输入层)
711 0
|
4月前
|
JavaScript
v-model指令获取常见表单项的内容 input,textarea,radio,checkbox,select
本文介绍了Vue.js中v-model指令的作用和语法,并通过示例代码展示了如何使用v-model来获取不同类型的表单元素(如文本输入框、文本域、复选框、单选框和下拉菜单)的内容。
|
8月前
|
JavaScript 编译器
组件 v-model
组件 v-model
|
8月前
|
前端开发
BootStrap让两个控件在一行显示(label和input同行)
BootStrap让两个控件在一行显示(label和input同行)
438 0
自定义组件使用v-model
自定义组件使用v-model
|
8月前
ant-design 设置Form.Item中的input框的值的方法
ant-design 设置Form.Item中的input框的值的方法
402 0
|
JavaScript 开发者
讲解 v-model 实现表单元素的数据双向绑定|学习笔记
快速学习讲解 v-model 实现表单元素的数据双向绑定
148 0
 讲解 v-model 实现表单元素的数据双向绑定|学习笔记
|
JavaScript 开发者
讲解v-model实现表单元素的数据双向绑定|学习笔记
快速学习讲解v-model实现表单元素的数据双向绑定
116 0
 讲解v-model实现表单元素的数据双向绑定|学习笔记