基于组件的库或框架(如 Vue )可以创建 可重用组件 ,它能在各自应用程序中相互传递数据,这些框架能确保这些数据是一致的,并且(希望)简化了它们的使用方式。
特别地,表单输入往往会有很多复杂性,我们希望把这些复杂性都隐藏在组件中,例如 自定义设计 、标签、验证、帮助消息等等,并且我们还要确保这些部分中的每一个都按正确的顺序排列渲染。
除此之外,Vue还有一个内置的 v-model
指令,通过绑定一个值并捕获输入事件来 模拟双向绑定 。如果要构建自定义输入组件,我们一定会想到直接使用 v-model
指令。
可悲的是,当我在 Vue 中查看单选按钮或复选框的自定义输入的示例时,他们根本没有考虑 v-model
,或者没有正确的使用。对于自定义文本输入有一些不错的文档,但由于它们没有解释自定义的单选框或复选框,我们将在本文进行讨论。
本教程旨在...
v-model
如何在原生输入上工作,主要侧重于单选框和复选框v-model
在自定义组件上的工作原理v-model
的工作原理开始之前的小提示 :整个代码示例中使用 ES2015+ 代码,我也会赞成使用 Vue.component
或 new Vue
的单一文件组件 语法。
v-model
是如何正常工作的?官方Vue文档 在这个话题上写得其实很不错,但是还有一些小盲点。无论如何,我们将会深入研究。
实质上, v-model
只是一个缩写的指令,它给我们提供了双向的数据绑定,代码是否缩写就取决于它使用的输入类型。
<input v-model="message" placeholder ="edit me"> <p>message: {{message}} </p> <!-- or --> <p>message: </p> <p style="white-space:pre-line"> {{message}} </p> <textarea v-model="message" placeholder="add multiple lines"></textarea>
当使用文本 input
(包括email
,number
等)或 textarea
时, v-model="varName"
等价于 :value="varName" @input="e => varName = e.target.value"
。 这意味着每次输入完成后的 varName
将被更新为输入的值,然后输入的值被设置为 varName
。 正常的 select
元素也会像这样,尽管 multiple
多项选择有所不同。
那么,单选按钮呢?
<input type="radio" value="One" v-model="pick"> <input type="radio" value="Two" v-model="pick"> <span>选择:{{pick}} </span>
这相当于:
<input type="radio" value="One" :checked="picked == 'One'" @change="e => pick = e.target.value"> <input type="radio" value="Two" :checked="picked == 'Two'" @change="e => pick = e.target.value"> <span>picked: {{pick}}</span>
注意 v-model
甚至没有触及 value
。它仍然在 change
事件的处理程序中做同样的事情(尽管现在是 change
而不是 input
),但是现在根据 picked
是否与该单选按钮的值相同来确定 checked
是 true 还是 false。
复选框有点难以谈论,因为它们有两种不同的行为,这取决于是否只有一个具有给定v-model
或多个的复选框。
如果您使用单个复选框,则 v-model
会将其视为布尔值,并忽略该 value
。
<input type="checkbox" value="foo" v-model="isChecked">
与以下代码相同
<input type="checkbox" value="foo" :checked="!!isChecked" @change="e => isChecked = e.target.checked">
如果想要它是非布尔值 ,可以使用 true-value
和 false-value
属性,它控制当选择复选框时,模型将被设置成什么值。
<input type="checkbox" value="foo" v-model="isChecked" true-value="1" false-value="0">
与以下代码相同
<input type="checkbox" value="foo" :checked="isChecked =='1'" @change="e => isChecked = e.target.checked?'1':'0'">
单一复选框的情况差不多就是这样。如果有多个复选框共享一个模型,那么这些复选框将填充一个数组,其值为所有勾选了的复选框,但一定要确保传入的模型是数组类型,否则会产生一些奇怪的行为。并且 true-value
和 false-value
属性不再有效。
<template> <div> <input type="checkbox" value="foo" v-model="checkedVals"> <input type="checkbox" value="bar" v-model="checkedVals"> <input type="checkbox" value="baz" v-model="checkedVals"> </div> </template> <script> export default { data:() =>({ checkedVals: ['bar'] }) } </script>
在模版中很难保证一致性,所以我将一些逻辑转移到组件的方法上:
<template> <div> <input type ="checkbox" value ="foo" v-model ="checkedVals"> <input type ="checkbox" value ="bar" v-model ="checkedVals"> <input type ="checkbox" value ="baz" v-model ="checkedVals"> </div> </template> <script> export default { data: () { return {checkedVals:['bar']} }, method: { shouldBeChecked(val) { return this.checkedVals.includes(val) }, updateVals(e) { let isChecked = e.target.checked let val = e.target.value if(isChecked) { this.checkedVals.push(val) } else { this.checkVals.splice(this.checkedVals.indexOf(val),1) } } } } </script>
这比我们以前看过的要复杂得多,但如果你把它分解下来,也不算太糟糕。当该复选框的值包含在数组中时, shouldBeChecked
为true
,否则为 false
。updateVals
将复选框中选中的值添加到数组,并且在取消选中时删除它。
v-model
如何在组件上工作?由于 Vue 不知道我们的组件应该如何工作,或者 Vue 试图作为某种输入类型的替代,v-model
会一致对待所有的组件。它实际上的工作方式与文本输入情况下完全相同,只是在事件处理程序中,它不会将事件对象传递给它,而是希望将值直接传递给它。 所以
<my-custom-component v-model="myProperty"/>
与以下代码相同
<my-custom-component :value="myProperty" @input="val => myProperty = val" />
组件中可以使用 model
属性改写为如下:
export default { name: 'my-custom-component', model: { prop:'foo', event:'bar' }, // ... }
v-model
将查看这些属性,而不会使用 value
属性。它将使用在 prop
指定的属性,而不是侦听 input
事件,它将使用在 event
中指定的 event
。 所以上面的 my-custom-component
示例实际上等价于以下代码:
<my-custom-component :foo ="myProperty" @bar="val => myProperty = val"/>
这很赞,但如果我们制作一个自定义单选或复选框,这还不够好。尽管通过这些工作,我们可以将 v-model
使用的逻辑转移到我们的定制组件中的单选和复选框。
v-model
的自定义单选框与复选框相比,定制单选框相当简单。以下是一个非常基本的自定义单选框,仅仅将 input
包装在标签中,并接受 label
属性来添加 label 文本。
<template> <label> <input type="radio" :checked="shouldBeChecked" :value="value" @change="updateInput"> {{ label }} </label> </template> <script> export default { model: { prop: 'modelValue', event: 'change' }, props: { value: { type: string, }, modelValue: { default: "" }, label: { type: string, required: true }, }, computed: { shouldBeChecked() { return this.modelValue == this.value } } method: { updateInput() { this.$emit('change',this.value) } } } </script>
注意 :为了有助于解释这些应用程序如何与 v-model
配合使用,我上面只用了 props
,但 input
标签还可以利用其他几个属性(例如 name
或 disabled
),因此请确保创建好了所需要的 props
并将其传递给 input
。还可以通过添加WAI-ARIA属性 ,以及使用slots 添加内容,而不是像上面在 label
里的 props。
由于本示例中没有包含 name
,可以认为一组单选框之间将不会实际上彼此同步。实际上,model 的更新将依次更新共享该 model 的其他单选按钮,因此只要共享相同的 model,他们就不需要像普通 HTML 表单一样分享一个共同的名字。
v-model
的自定义复选框使自定义复选框比单选按钮明显更复杂,主要是因为我们必须支持两种不同的用例:单个 true/false 复选框(可能使用或不使用 true-value
和/或 false-value
)和多个复选框将所有检查的值合并到一个数组中。
那么我们如何确定哪个用例呢?你可能会认为我们需要确定是否有其他复选框具有相同的 name
属性,但这并不是 Vue 的内置系统所使用的。就像单选框一样,Vue 根本不考虑 name
属性,它只是在本地提交表单时使用。那么你可能认为它会根据是否有其他复选框共享相同的 model 来确定,但也不是这样。它是由模型是否是数组来决定的,仅此而已。
因此,代码将按照自定义单选按钮的代码进行结构化,但是在内部的 shouldBeChecked
和 updateInput
将根据是否是一个数组而进一步细化。
<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 切换,一个用于选项列表。这将允许它更紧密地遵循单一责任原则,但如果你正在寻找选择框的替代品,那么这就是你正在寻找的(加上所有其他有用的属性和自定义功能的添加)。
本文转载自:众成翻译
译者:smartsrh 链接:http://www.zcfy.cc/article/3866
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有