利用vue组件自定义v-model实现一个Tab组件方法示例


Posted in Javascript onDecember 06, 2017

前言

最近在学习vue,今天看到自定义组件,纠结了一会会然后恍然大悟...官方教程写得不是很详细,所以我决定总结一下。下面话不多说了,来一起看看详细的介绍吧。

效果

先让我们看一下例子的效果吧!

利用vue组件自定义v-model实现一个Tab组件方法示例
v-model

我们知道 v-model 是 vue 里面的一个指令,vue的v-model是一个十分强大的指令,它可以自动让原生表单组件的值自动和你选择的值绑定,它可以用在 input 标签上,来做数据的双向绑定,就像这样:

<input v-model="tab">

v-model 事实上是一个语法糖,你也可以这么写:

<input :value="tab" :input="tab = $event.target.value">

可以看得出来,就是传进去一个参数 :value,监听一个事件 @input 而已。

如果有这样的需求,需要在自己的组件上使用 v-model,就像这样:

<Tab v-model="tab"></Tab>

如何来实现呢?

既然已经知道 v-model 是语法糖了,那么首先,我们可以知道在组件内得到的参数。

<!-- Tab.vue -->
<template>
 <div class="tab">
  <p>可以试着把这个值打印出来???</p>
  {{value}}
 </div>
</template>


<script>
 export default {
  props: {
   // ↓这个就是我们能取到的参数
   value: {
    type: String,
    default: ''
   }
  }
 }
</script>

嗯,先把这个 value 先放着,如果要实现例子的那个 Tab,还需要传进来一组选项(options):

<!-- example.vue -->
<template>
 <div>
  <!-- 这里多了一个参数 ↓ -->
  <Tab v-model="tab" :options="options"></Tab>
  <p class="info">{{tab}}</p>
 </div>
</template>

<script>
 import Tab from '~/Tab';

 export default {
  components: {
   Tab
  },
  data() {
   return {
    tab: 'bj',
    options: [{
     value: 'bj',
     text: '北京'
    }, {
     value: 'sh',
     text: '上海',
     disabled: true
    }, {
     value: 'gz',
     text: '广州'
    }, {
     value: 'sz',
     text: '深圳'
    }]
   }
  }
 }
</script>

那我们就把传进来的 options 循环出来吧!

<!-- Tab.vue -->
<template>
 <div class="tab">
  <div 
   class="item"
   v-for="(item, i) in options"
   :key="i">
   {{item.text}}
  </div>
 </div>
</template>

<script>
 export default {
  props: {
   value: {
    type: String
   },
   options: {
    type: Array,
    default: []
   }
  }
 }
</script>

传进来的 options 缺少些参数,我们每个选项需要 active 来标记是否是选中状态,需要 disabled 来标记是否是禁选状态,所以拷贝一个 currOptions 来补全不足参数。

另外直接改变 value 这个 props 是没有效果滴,所以拷贝一个 value 的副本,叫 currValue。

<!-- Tab.vue -->
<script>
 export default {
  props: {
   value: {
    type: String
   },
   options: {
    type: Array,
    default: []
   }
  },
  data() {
   return {
    // 拷贝一个 value
    currValue: this.value,
    currOptions: []
   }
  },
  mounted() {
   this.initOptions();
  },
  methods: {
   initOptions() {
    // 拷贝一个 options
    this.currOptions = this.options.map(item => {
     return {
      ...item,
      active: item.value === this.currValue,
      disabled: !!item.disabled
     }
    });
   }
  }
 }
</script>

?接下来再在选项上绑定击事件就 OK 了。

既然知道父组件会接受 input 事件,那我们就只需要 this.$emit('input', this.currValue); 就好了。

<!-- Tab.vue -->
<template>
 <div class="tab">
  <div 
   class="item"
   v-for="(item, i) in options"
   :key="i"
   @click="onTabSelect(item)">
   <!-- ↑ 这里绑定了一个事件! -->
   {{item.text}}
  </div>
 </div>
</template>

<script>
 export default {
  props: {
   value: {
    type: String
   },
   options: {
    type: Array,
    default: []
   }
  },
  data() {
   return {
    currValue: this.value,
    currOptions: []
   }
  },
  mounted() {
   this.initOptions();
  },
  methods: {
   initOptions() {
    this.currOptions = this.options.map(item => {
     return {
      ...item,
      active: item.value === this.currValue,
      disabled: !!item.disabled
     }
    });
   },
   // 添加选中事件
   onTabSelect(item) {
    if (item.disabled) return;
    this.currOptions.forEach(obj => obj.active = false);
    item.active = true;
    this.currValue = item.value;
    // 发布 input 事件,↓ 父组件如果有 v-model 就会监听到的。
    this.$emit('input', this.currValue);
   }
  }
 }
</script>

剩下的补上点样式还有 watch 下 value 和 options 的变化就可以了,最后贴上完整代码。

完整代码

<!-- example.vue -->
<template>
 <div>
  <Tab v-model="tab" :options="options"></Tab>
  <p class="info">{{tab}}</p>
 </div>
</template>

<script>
 import Tab from '~/Tab';

 export default {
  components: {
   Tab
  },
  data() {
   return {
    tab: 'bj',
    options: [{
     value: 'bj',
     text: '北京'
    }, {
     value: 'sh',
     text: '上海',
     disabled: true
    }, {
     value: 'gz',
     text: '广州'
    }, {
     value: 'sz',
     text: '深圳'
    }]
   }
  }
 }
</script>

<style lang="less" scoped>
 .info {
  margin-left: 50px;
  font-size: 30px;
 }
</style>
<!-- Tab.vue -->
<template>
 <div class="tab">
  <div 
   class="item"
   v-for="(item, i) in currOptions"
   :class="item | tabItemClass"
   :key="i"
   @click="onTabSelect(item)">
   {{item.text}}
  </div>
 </div>
</template>

<script>
 export default {
  props: {
   value: {
    type: String
   },
   options: {
    type: Array,
    default: []
   }
  },
  data() {
   return {
    currValue: this.value,
    currOptions: []
   }
  },
  mounted() {
   this.initOptions();
  },
  methods: {
   initOptions() {
    this.currOptions = this.options.map(item => {
     return {
      ...item,
      active: item.value === this.currValue,
      disabled: !!item.disabled
     }
    });
   },
   onTabSelect(item) {
    if (item.disabled) return;
    this.currOptions.forEach(obj => obj.active = false);
    item.active = true;
    this.currValue = item.value;
    this.$emit('input', this.currValue);
   }
  },
  filters: {
   tabItemClass(item) {
    let classList = [];
    if (item.active) classList.push('active');
    if (item.disabled) classList.push('disabled');
    return classList.join(' ');
   }
  },
  watch: {
   options(value) {
    this.initOptions();
   },
   value(value) {
    this.currValue = value;
   }
  }
 }
</script>

<style lang="less" scoped>
 .tab {
  @borderColor: #ddd;
  @radius: 5px;

  width: 100%;
  margin: 50px;
  overflow: hidden;
  position: relative;
  .item {
   padding: 10px 50px;
   border-top: 1px solid @borderColor;
   border-left: 1px solid @borderColor;
   border-bottom: 1px solid @borderColor;
   font-size: 30px;
   background-color: #fff;
   float: left;
   user-select: none;
   cursor: pointer;
   transition: 300ms;
   &:first-child {
    border-top-left-radius: @radius;
    border-bottom-left-radius: @radius;
   }
   &:last-child {
    border-right: 1px solid @borderColor;
    border-top-right-radius: @radius;
    border-bottom-right-radius: @radius;
   }
   &.active {
    color: #fff;
    background-color: red;
   }
   &:hover {
    color: #fff;
    background-color: #f06;
   }
   &.disabled {
    color: #fff;
    background-color: pink;
    cursor: no-drop;
   }
  }
 }
</style>

最后送上官网的链接→ 传送门

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js+FSO遍历文件夹下文件并显示
Mar 07 Javascript
javascript判断iphone/android手机横竖屏模式的函数
Dec 20 Javascript
原生js操作checkbox用document.getElementById实现
Oct 12 Javascript
jquery的父子兄弟节点查找示例代码
Mar 03 Javascript
jQuery scrollFix滚动定位插件
Apr 01 Javascript
js如何实现淡入淡出效果
Nov 18 Javascript
jQuery检测滚动条是否到达底部
Dec 15 Javascript
js重写方法的简单实现
Jul 10 Javascript
vue.js+Element实现表格里的增删改查
Jan 18 Javascript
js仿网易表单及时验证功能
Mar 07 Javascript
浅谈发布订阅模式与观察者模式
Apr 09 Javascript
详解无限滚动插件vue-infinite-scroll源码解析
May 12 Javascript
如何重置vue打印变量的显示方式
Dec 06 #Javascript
微信小程序实现点击按钮修改view标签背景颜色功能示例【附demo源码下载】
Dec 06 #Javascript
vue cli使用绝对路径引用图片问题的解决
Dec 06 #Javascript
微信小程序实现点击按钮移动view标签的位置功能示例【附demo源码下载】
Dec 06 #Javascript
实现单层json按照key字母顺序排序的示例
Dec 06 #Javascript
Thinkjs3新手入门之如何使用静态资源目录
Dec 06 #Javascript
浅谈JsonObject中的key-value数据解析排序问题
Dec 06 #Javascript
You might like
30 个很棒的PHP开源CMS内容管理系统小结
2011/10/14 PHP
PHP中设置一个严格30分钟过期Session面试题的4种答案
2014/07/30 PHP
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
2010/11/09 Javascript
Js 代码中,ajax请求地址后加随机数防止浏览器缓存的原因
2013/05/07 Javascript
将中国标准时间转换成标准格式的代码
2014/03/20 Javascript
javascript十六进制及二进制转化的方法
2015/05/06 Javascript
理解javascript中的严格模式
2016/02/01 Javascript
如何使用jquery实现文字上下滚动效果
2016/10/12 Javascript
JS button按钮实现submit按钮提交效果
2016/11/01 Javascript
AngularJS打开页面隐藏显示表达式用法示例
2016/12/25 Javascript
ligerUI---ListBox(列表框可移动的实例)
2017/11/28 Javascript
微信小程序云开发实现云数据库读写权限
2019/05/17 Javascript
node.js命令行教程图文详解
2019/05/27 Javascript
js实现图片粘贴到网页
2019/12/06 Javascript
vue新建项目并配置标准路由过程解析
2019/12/09 Javascript
Vue Object 的变化侦测实现代码
2020/04/15 Javascript
vue 微信分享回调iOS和安卓回调出现错误的解决
2020/09/07 Javascript
如何使用 JavaScript 操作浏览器历史记录 API
2020/11/24 Javascript
[01:03]DOTA2新的征程 你的脚印值得踏上
2014/08/13 DOTA
[04:31]2016国际邀请赛中国区预选赛妖精采访
2016/06/27 DOTA
Python下线程之间的共享和释放示例
2015/05/04 Python
python在不同层级目录import模块的方法
2016/01/31 Python
浅谈python实现Google翻译PDF,解决换行的问题
2018/11/28 Python
python pandas库的安装和创建
2019/01/10 Python
使用PyCharm官方中文语言包汉化PyCharm
2020/11/18 Python
使用layui实现左侧菜单栏及动态操作tab项的方法
2020/11/10 HTML / CSS
Tiqets荷兰:出售欧洲最美丽的景点和博物馆门票
2018/01/09 全球购物
Lampenwelt德国:欧洲领先的灯具和照明在线商店
2018/08/05 全球购物
机械专业毕业生自荐信
2013/11/02 职场文书
区域总监的岗位职责
2013/11/21 职场文书
建议书标准格式
2014/03/12 职场文书
春节联欢晚会主持词范文
2014/03/24 职场文书
有子女的离婚协议书怎么写(范本)
2014/09/29 职场文书
党员群众路线教育实践活动学习笔记
2014/11/05 职场文书
仓管员岗位职责
2015/02/03 职场文书
python pygame入门教程
2021/06/01 Python