前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在 Vue 中创建自定义输入

在 Vue 中创建自定义输入

作者头像
疯狂的技术宅
发布2019-03-28 10:58:06
6.3K0
发布2019-03-28 10:58:06
举报
文章被收录于专栏:京程一灯京程一灯

基于组件的库或框架(如 Vue )可以创建 可重用组件 ,它能在各自应用程序中相互传递数据,这些框架能确保这些数据是一致的,并且(希望)简化了它们的使用方式。

特别地,表单输入往往会有很多复杂性,我们希望把这些复杂性都隐藏在组件中,例如 自定义设计 、标签、验证、帮助消息等等,并且我们还要确保这些部分中的每一个都按正确的顺序排列渲染。

除此之外,Vue还有一个内置的 v-model 指令,通过绑定一个值并捕获输入事件来 模拟双向绑定 。如果要构建自定义输入组件,我们一定会想到直接使用 v-model 指令。

可悲的是,当我在 Vue 中查看单选按钮或复选框的自定义输入的示例时,他们根本没有考虑 v-model ,或者没有正确的使用。对于自定义文本输入有一些不错的文档,但由于它们没有解释自定义的单选框或复选框,我们将在本文进行讨论。

本教程旨在...

  1. 了解 v-model 如何在原生输入上工作,主要侧重于单选框和复选框
  2. 默认情况下,了解 v-model 在自定义组件上的工作原理
  3. 了解如何创建自定义复选框和单选,以模拟原生 v-model 的工作原理

开始之前的小提示 :整个代码示例中使用 ES2015+ 代码,我也会赞成使用 Vue.componentnew Vue 的单一文件组件 语法。

v-model 是如何正常工作的?

官方Vue文档 在这个话题上写得其实很不错,但是还有一些小盲点。无论如何,我们将会深入研究。

实质上, v-model 只是一个缩写的指令,它给我们提供了双向的数据绑定,代码是否缩写就取决于它使用的输入类型。

文本框

代码语言:javascript
复制
<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(包括emailnumber等)或 textarea 时, v-model="varName"等价于 :value="varName" @input="e => varName = e.target.value" 。 这意味着每次输入完成后的 varName 将被更新为输入的值,然后输入的值被设置为 varName 。 正常的 select 元素也会像这样,尽管 multiple 多项选择有所不同。

单选按钮

那么,单选按钮呢?

代码语言:javascript
复制
<input type="radio" value="One" v-model="pick"> <input type="radio" value="Two" v-model="pick"> <span>选择:{{pick}} </span> 

这相当于:

代码语言:javascript
复制
<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

代码语言:javascript
复制
<input type="checkbox" value="foo" v-model="isChecked"> 

与以下代码相同

代码语言:javascript
复制
<input type="checkbox" value="foo" :checked="!!isChecked" @change="e => isChecked = e.target.checked"> 

如果想要它是非布尔值 ,可以使用 true-valuefalse-value 属性,它控制当选择复选框时,模型将被设置成什么值。

代码语言:javascript
复制
<input type="checkbox" value="foo" v-model="isChecked" true-value="1" false-value="0"> 

与以下代码相同

代码语言:javascript
复制
<input type="checkbox" value="foo" :checked="isChecked =='1'" @change="e => isChecked = e.target.checked?'1':'0'"> 

单一复选框的情况差不多就是这样。如果有多个复选框共享一个模型,那么这些复选框将填充一个数组,其值为所有勾选了的复选框,但一定要确保传入的模型是数组类型,否则会产生一些奇怪的行为。并且 true-valuefalse-value 属性不再有效。

代码语言:javascript
复制
<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>  

在模版中很难保证一致性,所以我将一些逻辑转移到组件的方法上:

代码语言:javascript
复制
<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> 

这比我们以前看过的要复杂得多,但如果你把它分解下来,也不算太糟糕。当该复选框的值包含在数组中时, shouldBeCheckedtrue ,否则为 falseupdateVals将复选框中选中的值添加到数组,并且在取消选中时删除它。

v-model 如何在组件上工作?

由于 Vue 不知道我们的组件应该如何工作,或者 Vue 试图作为某种输入类型的替代,v-model 会一致对待所有的组件。它实际上的工作方式与文本输入情况下完全相同,只是在事件处理程序中,它不会将事件对象传递给它,而是希望将值直接传递给它。 所以

代码语言:javascript
复制
<my-custom-component v-model="myProperty"/> 

与以下代码相同

代码语言:javascript
复制
<my-custom-component :value="myProperty" @input="val => myProperty = val" /> 

组件中可以使用 model 属性改写为如下:

代码语言:javascript
复制
export default {   name: 'my-custom-component',   model: {     prop:'foo',     event:'bar'   }, // ... } 

v-model 将查看这些属性,而不会使用 value 属性。它将使用在 prop 指定的属性,而不是侦听 input 事件,它将使用在 event中指定的 event。 所以上面的 my-custom-component 示例实际上等价于以下代码:

代码语言:javascript
复制
<my-custom-component :foo ="myProperty" @bar="val => myProperty = val"/> 

这很赞,但如果我们制作一个自定义单选或复选框,这还不够好。尽管通过这些工作,我们可以将 v-model 使用的逻辑转移到我们的定制组件中的单选和复选框。

支持 v-model 的自定义单选框

与复选框相比,定制单选框相当简单。以下是一个非常基本的自定义单选框,仅仅将 input 包装在标签中,并接受 label 属性来添加 label 文本。

代码语言:javascript
复制
<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 标签还可以利用其他几个属性(例如 namedisabled ),因此请确保创建好了所需要的 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 来确定,但也不是这样。它是由模型是否是数组来决定的,仅此而已。

因此,代码将按照自定义单选按钮的代码进行结构化,但是在内部的 shouldBeCheckedupdateInput 将根据是否是一个数组而进一步细化。

代码语言:javascript
复制
<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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • v-model 是如何正常工作的?
    • 文本框
      • 单选按钮
        • 复选框
        • v-model 如何在组件上工作?
          • 支持 v-model 的自定义单选框
          • 支持 v-model 的自定义复选框
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档