专栏首页京程一灯在 Vue 中创建自定义输入

在 Vue 中创建自定义输入

基于组件的库或框架(如 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 只是一个缩写的指令,它给我们提供了双向的数据绑定,代码是否缩写就取决于它使用的输入类型。

文本框

<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 多项选择有所不同。

单选按钮

那么,单选按钮呢?

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

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

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 标签还可以利用其他几个属性(例如 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 将根据是否是一个数组而进一步细化。

<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

本文分享自微信公众号 - 京程一灯(jingchengyideng),作者:smartsrh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-08-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解决 JavaScript 中处理 null 和 undefined 的麻烦事[每日前端夜话0xE6]

    许多 JavaScript 开发人员正在为怎么处理可选值头痛。有什么好办法来最大程度地减少由值(可能为 null、undefined或在运行时未初始化)引起的错...

    疯狂的技术宅
  • 深入探讨 Undefined [每日前端夜话(0x0E)]

    Undefined这个概念听起来很简单,不过你知道应该怎样检查JavaScript中的变量或属性是否真的存在吗? 做这件事最好的方法是什么? 我们如何涵盖所有的...

    疯狂的技术宅
  • Javascript 的新功能-Part 1[每日前端夜话0xC6]

    最近更新的 V8 引擎使性能提升了不少。JavaScript 解析速度提高了 2 倍甚至更快,从node v8.0开始,node v11以上版本的平均速度比 ...

    疯狂的技术宅
  • 用 :key 管理可复用元素

    我们会发现,在点击按钮切换登录方式后,输入框中已有的内容没有被清除,这是为什么呢?

    Chor
  • javascript -- 数据类型

    大当家
  • 再谈 API 的撰写 - 子系统

    在做一个系统时,有一些子系统几乎是必备的:配置管理,CLI,以及测试框架。 配置管理 我们先说配置管理。一个系统的灵活度,和它的配置管理是离不开的。系统中存在的...

    tyrchen
  • SpringBoot实现通用的接口参数校验,注解实战

    本文介绍基于 SpringBoot和 JDK8编写一个结合自定义注解实现通用的接口参数校验。

    搜云库技术团队
  • 你需要知道的几类npm依赖包管理

    用户1687375
  • Android面向切面AOP架构设计后续补充

    假设 test 类里有使用到 @aop 的切点注解,那么我们在混淆文件中就应该 -keep 这个 test 类

    萬物並作吾以觀復
  • [机器学习从零到壹sklearn]·0.1.1·线性拟合

    小宋是呢

扫码关注云+社区

领取腾讯云代金券