利用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将搜索的关键字高亮显示实现代码
Nov 08 Javascript
javascript中使用正则计算中文长度的例子
Apr 29 Javascript
javascript对中文按照拼音排序代码
Aug 20 Javascript
js父页面与子页面不同时显示的方法
Oct 16 Javascript
分享两个手机访问pc网站自动跳转手机端网站代码
Dec 24 Javascript
jQuery使用EasyUi实现三级联动下拉框效果
Mar 08 Javascript
微信小程序tabbar不显示解决办法
Jun 08 Javascript
mescroll.js上拉加载下拉刷新组件使用详解
Nov 13 Javascript
详解Vue-cli3 项目在安卓低版本系统和IE上白屏问题解决
Apr 14 Javascript
微信小程序去除左上角返回键的实现方法
Mar 06 Javascript
javascript利用键盘控制小方块的移动
Apr 20 Javascript
vue 微信分享回调iOS和安卓回调出现错误的解决
Sep 07 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
用PHP和ACCESS写聊天室(七)
2006/10/09 PHP
dedecms模板标签代码官方参考
2007/03/17 PHP
用PHP获取Google AJAX Search API 数据的代码
2010/03/12 PHP
对PHP语言认识上需要避免的10大误区
2014/06/12 PHP
php语言中使用json的技巧及json的实现代码详解
2015/10/27 PHP
PHP微信API接口类
2016/08/22 PHP
PHP页面跳转操作实例分析(header方法)
2016/09/28 PHP
实例讲解php实现多线程
2019/01/27 PHP
jQuery父级以及同级元素查找介绍
2013/09/04 Javascript
jquery实现图片滚动效果的简单实例
2013/11/23 Javascript
js实现的早期滑动门菜单效果代码
2015/08/27 Javascript
JS弹出对话框实现方法(三种方式)
2015/12/18 Javascript
基于jquery实现瀑布流布局
2020/06/28 Javascript
利用Chrome DevTools直接调试Node.js和JavaScript的方法详解(并行)
2017/02/16 Javascript
Vue组件化通讯的实例代码
2017/06/23 Javascript
jQuery的时间datetime控件在AngularJs中的使用实例(分享)
2017/08/17 jQuery
JS实现的简单表单验证功能示例
2017/10/13 Javascript
cocos2dx+lua实现橡皮擦功能
2018/12/20 Javascript
JS中创建自定义类型的常用模式总结【工厂模式,构造函数模式,原型模式,动态原型模式等】
2019/01/19 Javascript
vuex存取值和映射函数使用说明
2020/07/24 Javascript
[32:17]完美世界DOTA2联赛循环赛LBZS vs Forest第二场 10月30日
2020/10/31 DOTA
python打开文件并获取文件相关属性的方法
2015/04/23 Python
python中Pycharm 输出中文或打印中文乱码现象的解决办法
2017/06/16 Python
Django如何实现内容缓存示例详解
2017/09/24 Python
PyQt5每天必学之布局管理
2018/04/19 Python
使用matplotlib动态刷新指定曲线实例
2020/04/23 Python
一篇文章教你用python画动态爱心表白
2020/11/22 Python
让IE支持HTML5的方法
2012/12/11 HTML / CSS
HTML5实现晶莹剔透的雨滴特效
2014/05/14 HTML / CSS
澳大利亚商务邀请函
2014/01/17 职场文书
高三自我评价
2014/02/01 职场文书
扩大国家免疫规划实施方案
2014/03/21 职场文书
党支部群众路线整改措施思想汇报
2014/10/10 职场文书
社区四风存在问题及整改措施
2014/10/26 职场文书
解决Pytorch中关于model.eval的问题
2021/05/22 Python
MySQL提取JSON字段数据实现查询
2022/04/22 MySQL