溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用vue組件自定義v-model實(shí)現(xiàn)一個(gè)Tab組件

發(fā)布時(shí)間:2021-07-26 09:24:53 來(lái)源:億速云 閱讀:198 作者:小新 欄目:web開(kāi)發(fā)

這篇文章主要介紹如何使用vue組件自定義v-model實(shí)現(xiàn)一個(gè)Tab組件,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

效果

先讓我們看一下例子的效果吧!

如何使用vue組件自定義v-model實(shí)現(xiàn)一個(gè)Tab組件
v-model

我們知道 v-model 是 vue 里面的一個(gè)指令,vue的v-model是一個(gè)十分強(qiáng)大的指令,它可以自動(dòng)讓原生表單組件的值自動(dòng)和你選擇的值綁定,它可以用在 input 標(biāo)簽上,來(lái)做數(shù)據(jù)的雙向綁定,就像這樣:

<input v-model="tab">

v-model 事實(shí)上是一個(gè)語(yǔ)法糖,你也可以這么寫(xiě):

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

可以看得出來(lái),就是傳進(jìn)去一個(gè)參數(shù) :value,監(jiān)聽(tīng)一個(gè)事件 @input 而已。

如果有這樣的需求,需要在自己的組件上使用 v-model,就像這樣:

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

如何來(lái)實(shí)現(xiàn)呢?

既然已經(jīng)知道 v-model 是語(yǔ)法糖了,那么首先,我們可以知道在組件內(nèi)得到的參數(shù)。

<!-- Tab.vue -->
<template>
 <div class="tab">
  <p>可以試著把這個(gè)值打印出來(lái)???</p>
  {{value}}
 </div>
</template>


<script>
 export default {
  props: {
   // ↓這個(gè)就是我們能取到的參數(shù)
   value: {
    type: String,
    default: ''
   }
  }
 }
</script>

嗯,先把這個(gè) value 先放著,如果要實(shí)現(xiàn)例子的那個(gè) Tab,還需要傳進(jìn)來(lái)一組選項(xiàng)(options):

<!-- example.vue -->
<template>
 <div>
  <!-- 這里多了一個(gè)參數(shù) ↓ -->
  <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>

那我們就把傳進(jìn)來(lái)的 options 循環(huán)出來(lái)吧!

<!-- 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>

傳進(jìn)來(lái)的 options 缺少些參數(shù),我們每個(gè)選項(xiàng)需要 active 來(lái)標(biāo)記是否是選中狀態(tài),需要 disabled 來(lái)標(biāo)記是否是禁選狀態(tài),所以拷貝一個(gè) currOptions 來(lái)補(bǔ)全不足參數(shù)。

另外直接改變 value 這個(gè) props 是沒(méi)有效果滴,所以拷貝一個(gè) value 的副本,叫 currValue。

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

?接下來(lái)再在選項(xiàng)上綁定擊事件就 OK 了。

既然知道父組件會(huì)接受 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)">
   <!-- ↑ 這里綁定了一個(gè)事件! -->
   {{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;
    // 發(fā)布 input 事件,↓ 父組件如果有 v-model 就會(huì)監(jiān)聽(tīng)到的。
    this.$emit('input', this.currValue);
   }
  }
 }
</script>

剩下的補(bǔ)上點(diǎn)樣式還有 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>

以上是“如何使用vue組件自定義v-model實(shí)現(xiàn)一個(gè)Tab組件”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI