利用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 相关文章推荐
jQuery 扩展对input的一些操作方法
Oct 30 Javascript
jQuery Ajax请求状态管理器打包
May 03 Javascript
JavaScript面向对象知识串结(读JavaScript高级程序设计(第三版))
Jul 17 Javascript
Underscore.js 的模板功能介绍与应用
Dec 24 Javascript
JavaScript获取网页、浏览器、屏幕高度和宽度汇总
Dec 18 Javascript
jQuery隐藏和显示效果实现
Apr 06 Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
Jun 23 Javascript
详解Bootstrap的iCheck插件checkbox和radio
Aug 24 Javascript
vue 系列——vue2-webpack2框架搭建踩坑之路
Dec 22 Javascript
解决vuex刷新状态初始化的方法实现
Aug 15 Javascript
微信小程序实现上拉加载功能
Nov 20 Javascript
Ajax异步刷新功能及简单案例
Nov 20 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
完整删除ecshop中获取店铺信息的API
2014/12/24 PHP
PHP Echo字符串的连接格式
2016/03/07 PHP
PHP实现数组根据某个单元字段排序操作示例
2018/08/01 PHP
js 字符串操作函数
2009/07/25 Javascript
模拟多级复选框效果的jquery代码
2013/08/13 Javascript
JS获取url链接字符串 location.href
2013/12/23 Javascript
jQuery 选择器详解
2015/01/19 Javascript
javascript数据结构之双链表插入排序实例详解
2015/11/25 Javascript
JavaScript仿淘宝页面图片滚动加载及刷新回顶部的方法解析
2016/05/24 Javascript
jQuery 3.0十大新特性
2016/07/06 Javascript
jquery 实时监听输入框值变化的完美方法(必看)
2017/01/26 Javascript
Bootstrap进度条学习使用
2017/02/09 Javascript
bootstrap table插件的分页与checkbox使用详解
2017/07/23 Javascript
JS基于封装函数实现的表格分页完整示例
2018/06/26 Javascript
从零学Python之入门(二)基本数据类型
2014/05/25 Python
跟老齐学Python之开始真正编程
2014/09/12 Python
Python随机生成均匀分布在单位圆内的点代码示例
2017/11/13 Python
Python set常用操作函数集锦
2017/11/15 Python
2018年Python值得关注的开源库、工具和开发者(总结篇)
2018/01/04 Python
python批量下载抖音视频
2019/06/17 Python
python算法题 链表反转详解
2019/07/02 Python
python编写猜数字小游戏
2019/10/06 Python
Python +Selenium解决图片验证码登录或注册问题(推荐)
2020/02/09 Python
jupyter notebook 多行输出实例
2020/04/09 Python
Python使用Pyqt5实现简易浏览器(最新版本测试过)
2020/04/27 Python
matplotlib相关系统目录获取方式小结
2021/02/03 Python
CSS3模拟动画下拉菜单效果
2017/04/12 HTML / CSS
加拿大户外探险购物网站:SAIL
2020/06/27 全球购物
亿阳信通股份有限公司笔试题(C#)
2016/03/04 面试题
经贸日语专业个人求职信
2013/12/13 职场文书
简历中的自我评价范文
2014/02/05 职场文书
优秀广告词大全
2014/03/19 职场文书
大学生感恩父母演讲稿
2014/08/28 职场文书
2015年小学生国庆节演讲稿
2015/07/30 职场文书
公司规章制度范本
2015/08/03 职场文书
Python基础知识之变量的详解
2021/04/14 Python