首页 > 编程 > JavaScript > 正文

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

2019-11-19 14:46:47
字体:
来源:转载
供稿:网友

前言

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

效果

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


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>

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

总结

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表