Vue 2.0学习笔记:自定义表单组件

发布于 大漠

上一节中,通过v-model的学习,我们可以实现双向数据绑定的的效果。在整个教程中,我们看到的示例都是表单控件方面的。实际上v-model还可以和组件结合在一起实现双向数据的绑定效果。

在Web的表单控件中,我们经常为了一些特殊的视觉效果,做自定义的表单风格,比如单选按钮、复选框和下拉选择框之类的。那么我们通过Vue来做这些表单控件的组件,会让我们变得更为轻松,而且一劳永逸。接下来我们看看怎么实现单选按钮和复选框的组件。

自定义表单组件

自定义单选按钮

与复选框相比,定制单选安扭相对而言要简单。以下是一个非常基本的自定义单选按钮。将inputlabel包装在标签中。下面这个示例是来自@InvokeVue单选按钮

我们把整个Demo写在Codepen上,以便大家更为方便的查阅代码。

创建radio组件,代码如下:

Vue.component('radio',{
    template: `
        <div class="custom-radio" v-bind:class="{ inverted: inverted }">
            <input type="radio" v-bind:name="name" v-bind:class="className" v-bind:id="id" v-bind:checked="checked" v-bind:value="value" v-bind:required="required" v-on:change="updateInput" />
            <label v-bind:for="id">{{ label }}</label>
        </div>
    `,
    props: {
        name: {
            type: String,
            required: false
        }, 
        className: {
            type: String,
            required: false
        },
        id: {
            type: String,
            required: false
        },
        value: {
            type: String,
            required: false
        },
        required: {
            type: Boolean,
            required: false,
            default: false
        },
        checked: {
            type: Boolean,
            required: false,
            default: false
        },
        label: {
            type: String,
            required: true
        },
        inverted: {
            type: Boolean,
            required: false,
            default: false
        }
    },
    methods: {
        updateInput: function(event) {
            this.$emit('input', event.target.value);
        }
    }
})

通过Vue.component()创建了一个radio组件,同时把组件需要的模板(HTML)结构定义在template中:

<div class="custom-radio" v-bind:class="{ inverted: inverted }">
    <input type="radio" v-bind:name="name" v-bind:class="className" v-bind:id="id" v-bind:checked="checked" v-bind:value="value" v-bind:required="required" v-on:change="updateInput" />
    <label v-bind:for="id">{{ label }}</label>
</div>

其实除了上面的方式之外,还可以单独创建一个radio.vue文件,在这个文件中通过<template>来声明组件所需要的标签元素:

<template>
    <div class="custom-radio" v-bind:class="{ inverted: inverted }">
        <input type="radio" v-bind:name="name" v-bind:class="className" v-bind:id="id" v-bind:checked="checked" v-bind:value="value" v-bind:required="required" v-on:change="updateInput" />
        <label v-bind:for="id">{{ label }}</label>
    </div>
</tempalte>

组件对应需要的逻辑代码放置在<script>中:

<script>
    export default {
        props: {
            name: {
                type: String,
                required: false
            }, 
            className: {
                type: String,
                required: false
            },
            id: {
                type: String,
                required: false
            },
            value: {
                type: String,
                required: false
            },
            required: {
                type: Boolean,
                required: false,
                default: false
            },
            checked: {
                type: Boolean,
                required: false,
                default: false
            },
            label: {
                type: String,
                required: true
            },
            inverted: {
                type: Boolean,
                required: false,
                default: false
            }
        },
        methods: {
            updateInput: function(event) {
                this.$emit('input', event.target.value);
            }
        }
    }
</script>

我们创建了所需的props并将其传递给input。比如单选按钮常见的一些属性,比如namecheckedid等。除些之外,还可以添加WAI-ARIA属性,以及使用slots添加内容,而不是像上面在label里的props

props中定义了单选按钮组件需要的一些属性,另外在methods中创建了一个updateInput方法,用来监听input的变化。

这样一来,咱们就完成了单选按钮radio组件的创建,我们可以这样使用:

<!-- HTML -->
<div id="app">
    <radio name="method" value="phone" label="Phone" id="phone"></radio>
    <radio name="method" value="email" label="Email" id="email"></radio>
</div>

// JavaScript
let app = new Vue({
    el: '#app'
})

除了这样的方式之外,还可以这样使用:

<template>
    <div id="app">
        <Radio name="method" value="phone" label="Phone" id="phone"/>
    </div>
</tempalte>
<script>
    import Radio from 'radio.vue'
    export default {
        components: {
            Radio
        }
    }
</script>

<style>
    /* 添加样式 */
</style>

为了节约篇幅,样式代码就不贴上来了。最终的效果如下:

自定义复选框

使用自定义复选框比单选按钮明显要更复杂,主要是因为我们必须支持两种不同的用例:单个true/false复选框(可能使用或不使用true-value和/或false-value)和多个复选框将所有检查的值合并到一个数组中。

那么我们如何确定哪个用例呢?你可能会认为我们需要确定是否有其他复选框具有相同的name属性,但这并不是Vue的内置系统所使用的。就像单选框一样,Vue根本不考虑name属性,它只是在本地提交表单时使用。那么你可能认为它会根据是否有其他复选框共享相同的model来确定,但也不是这样。它是由模型是否是数组来决定的,仅此而已。

来看一个复选框按钮的组件代码:

<template> 
    <label> 
        <input type="checkbox" :checked="shouldBeChecked" :value="value" @change="updateInput"> 
        {{ label }} 
    </label> 
</template> 
<script>  
    export default {  
        model: {  
            prop: 'modelValue',  
            event: 'change'  
        },  
        props:  {  
            value: {  
                type:  String,   
            },   
            modelValue: {   
                default: false   
            },   
            label:  {   
                type:  String,   
                required: true   
            },   
            // 我们将 `true-value` 和 `false-value` 设置为 true 和 false   
            // 我们可以随时使用它们,而不用检查它们是否被设置。   
            // 也可以在这里使用驼峰命名,但要用连字符分隔属性名称   
            // 使用组件时仍然有效   
            trueValue: {   
                default: true   
            },   
            falseValue: {   
                default: false   
            }   
        },   
        computed: {   
            shouldBeChecked() {   
                if(this.modelValue instanceof Array){   
                return this.modelValue.includes(this.value)   
                }   
                // 请注意,"true-value" 和 "false-value" 是 JS 中的驼峰命名  
                return this.modelValue === this.trueValue   
            }   
        },   
        method: {   
            updateInput(event) {   
                let isChecked = event.target.checked   

                if(this.modelValue instanceof Array){   
                    let newValue = [... this.modelValue]   

                    if(isChecked){   
                        newValue.push(THIS.VALUE)   
                    } else {   
                        newValue.splice(newValue.indexOf(this.value),1)   
                    }   
                    this.$emit('change',newValue)   
                } else {   
                    this.$emit('change',isChecked ? this.trueValue : this.falseValue)   
                }   
            }   
        }   
    }   
</script>

这就完成啦。但是将其分解成两个不同的组件可能会更好:一个用于处理单个 true/false 切换,一个用于选项列表。这将允许它更紧密地遵循单一责任原则,但如果你正在寻找选择框的替代品,那么这就是你正在寻找的(加上所有其他有用的属性和自定义功能的添加)。

@Invoke也提供了一个Vue复选框组件checkbox。代码就不贴了,其和radio组件的代码有点类似,直接上效果吧,如果想看代码,可以在Codepen上查看:

对于自定单选按钮和复选框,其功能上的代码都相近,你只需要在CSS样式上进修改。就能得到不同的效果。比如上面的两个示例。当然,还可以做一些其他的功能,比如@CSWApps写的Bootstrap的单选按钮组组件:

这篇文章涉及到了Vue的组件相关知识,但我们从未接触过组件这方面相关的知识,或许上面有些代码还不能明白是起什么作用,但不用担心,我们现在只要知道这样做就能有这样的效果就可以,至于原理和为什么,我们后面会搞懂的。

总结

这篇文章介绍了怎么使用Vue实现自定义的表单的单选按钮和复选框两个组件。并且通过v-model实现双向数据绑定。在表单控件中,除了单选按钮和复选框之外,还有其他表单控件,比如inputselect等控件。那么我们可以使用类似的方法实现自定义的表单组件。如果你感兴趣的话,自己动手写一个下拉选择框的组件。有关于Vue的组件和Vue相关的资料,也可以看看:

  • Awesome-Vue 组件集:Awesome-Vue 是一个巨大的 Vue 相关项目和资源列表,随时想看该列表中的任何内容都可以,但是我想特别指出 UI 库和组件集,因为它们几乎有所有的单复选框的例子,可以看看,还可以研究他们的源代码。
  • Vue Curated:这是一个类似于 Awesome-Vue 的列表,但经过了更严格的筛选,所以列表中的所有内容都值得一看。
  • Vue组件指南:官方 Vue 指南是了解有关 Vue 相关基础知识的好地方。
  • Vue API文档:本文档介绍了 Vue 的深入细节。

作者是Vue初学者,对Vue的理解还不到家,如果文章中有何不对,还请大婶拍正。如果你有这方面的经验,欢迎在下面的平论中与我们一起分享。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/vue/custom-radio-and-checkbox-component-with-vue.htmlAir Max 1 Ultra Essential