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

前言

最近在学习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>

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

总结

以上是 利用vue组件自定义v-model实现一个Tab组件方法示例 的全部内容, 来源链接: utcz.com/z/339909.html

回到顶部