vue中v-model如何绑定多循环表达式实战案例

作者:Dignity_呱 时间:2024-04-09 10:59:42 

一、存在问题

v-model想绑定表达式 || 函数方法,发现控制台报错了,不允许这波操作。

下面我们分析存在该问题的原因和解决方法。

实战经验。

二、还原场景

有这样子的数组对象结构

const arr = [
 { value: 'a' }, { value: 'b' }
]

const item {
 a: 1,
 b: 2,
 __config__:{ required: false },
}

想循环arr数组,然后通过arr.value去找item里面的属性,然后绑定在v-model上。

理想效果是

arr我们有两个对象,就循环两个出来,通过arr[0].value去寻找item的属性。

<div>
 <van-switch v-model="item[arr[0].value]" />
 <van-switch v-model="item[arr[1].value]" />
</div>

于是,我们可以很顺利的😁写出遍历

<div
 v-for="(cell, cellKey) in arr"
 :key="cellKey"
>
 <van-switch v-model="item[cell.value]"  />
</div>

需求提升

前面只是为下面做铺垫,其实真实的场景会更复杂点。

数组对象结构是这样子的:

想通过arr.value去找item里面的属性,然后绑定在v-model上。

现在的arr数组的对象不再是一层了,而是多层,如:

{ value: '__config__.required' }

这样的结构怎么在template上遍历呢?

const arr = [
 { value: 'a' }, { value: '__config__.required' }
]

const item {
 a: 1,
 b: 2,
 __config__:{ required: false },
}

三、进行分析

3.1 直接赋值法

于是,有了第一个想法,就是arr数组写着:

{ value: '__config__.required' }

直接看v-for遍历出来是怎么样的。

<div
 v-for="(cell, cellKey) in arr"
 :key="cellKey"
>
 <van-switch v-model="item[cell.value]"  />
</div>

他不是在__config__对象中去设置required,而是把__config__.required当做一个属性了。

vue中v-model如何绑定多循环表达式实战案例

v-model 不仅仅是语法糖,它还有副作用。

如果 v-model 绑定的是响应式对象上某个不存在的属性,那么 vue 会悄悄地增加这个属性,并让它响应式。

很明显,这个方法不行❌,只适用于item.a,不能支持多层item.__config__.required

再尝试另外一种方法🚶。

3.2 通过方法解析

我想在v-model绑定一个函数专门来让其value里面的'__config__.required'取出。

理想是通过一个方法,比如叫_get方法,这个方法可以让他处理成item['__config__']['required'],这样子我们就可以取到正确的值了。

开干!

* @description 实现 lodash 的_.get
*
* @param {Array/Object} source 目标数组/对象
* @param {String} path 获取对象的字符串路径
* @param {*} [defaultValue=undefined] 若值为空,则可传入默认值作为返回值
* @return {*}
* @example
* const get1 = tool._get({ a: null }, "a.b.c", 3)       // output: 3
 const get2 = tool._get({ a: [{ b: 1 }] }, "a[0].b")   // output: 1
*/
_get(source, path, defaultValue = undefined) {
// a[3].b -> a.3.b -> [a,3,b]
// path 中也可能是数组的路径,全部转化成 . 运算符并组成数组
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".")
let result = source
for (const p of paths) {
  // 注意 null 与 undefined 取属性会报错,所以使用 Object 包装一下。
  result = Object(result)[p]
  if (result == undefined) {
    return defaultValue
  }
}
return result
}

然后在模板里面遍历,通过_get解析取值。

<div
 v-for="(cell, cellKey) in arr"
 :key="cellKey"
>
 <van-switch v-model="_get(item, cell.value)"  />
</div>

data(){
  return {
   const arr = [
     { value: 'a' }, { value: '__config__.required' }
   ]

const item {
     a: 1,
     b: 2,
     __config__:{ required: false },
   }
  }
}

发现控制台报错了。

v-model‘ directives require the attribute value which is valid as LHS

📖中文翻译: &ldquo;v-model&rdquo;指令要求属性值必须与LHS一样有效

❓原因:v-model将始终把Vue实例的data视为数据真实的来源,你应该在组件的JavaScript里的data中声明初始值,这意味着你不能要求v-model一次观察多个变量。

通俗易懂的说:你不能在v-model绑定函数或者表达式。

看来,这个方法也❌行不通,继续探索解决方案🚶。

3.3 通过computed计算属性

v-model 的值只能是一个变量。

值得一提,v-model可以绑定计算属性的值,因为计算属性也是data数据真实的数据,是一个变量。

那我们就可以在computed计算属性里面去通过_get方法解析取值。

<div
 v-for="(cell, cellKey) in arr"
 :key="cellKey"
>
 <van-switch v-model="getItem"  />
</div>

computed:{
   getItem(){
       return _get(this.item, 写啥)
   }
}

我们可以发现,写不下去了,如果没有v-for遍历,只是一个值的话,就可以。

但是,咱们这里的遍历的,我们不能在遍历的时候,传参数给计算属性getItem,传了就变成方法了,就会报错。

计算属性可以用,但在v-model不能传参。

<van-switch v-model="getItem"  />

// 我们不能写
<van-switch v-model="getItem(item, cell.value)"  />

看来,计算属性在我们这里是❌行不通了,但计算属性可以解决我们v-model不能去调用方法的问题。

计算属性还是可以很好的解决单个值写表达式的。

3.4 two value

我想到一个最傻的方案。

就是写两个value,然后判断一下,如下代码:

<div
 v-for="(cell, cellKey) in arr"
 :key="cellKey"
>
   <template v-if="cell.value1">
       <van-switch v-model="item[cell.value][cell.value1]"  />
   </template>
   <template v-else>
       <van-switch v-model="item[cell.value]" />
   </template>
</div>

data(){
  return {
   const arr = [
     { value: 'a' }, { value: '__config__', value1: 'required' }
   ]

const item {
     a: 1,
     b: 2,
     __config__:{ required: false },
   }
  }
}

如果可以写表达式,里面就不需要写两条了,直接写:

<van-switch
 v-model="cell.value1 ? item[cell.value][cell.value1] : item[cell.value]"  
/>

当然,直接写个方法来解析获取__config__.required会更好。

3.5 父子组件 + 计算属性

有了以上的实践,我们可以来总结一下目前的情况:

  • 可以使用计算属性写表达式。

  • 但局限于,我们的场景是循环的,那我们使用计算属性的话需要传参,但一旦传参则认为是方法,v-model无法支持。

我们可以想到,可以创建一个新的组件,然后组件内接收每次循环的item,在新的组件里面写v-model绑定计算属性。

Perfect完美。

代码如下:

父组件

<div v-for="(cell, cellKey) in arr" :key="cellKey">
   <Item :item="item" :cell="cell" />
</div>

data(){
  return {
   const arr = [
     { value: 'a' }, { value: '__config__', value1: 'required' }
   ]

const item {
     a: 1,
     b: 2,
     __config__:{ required: false },
   }
  }
}

子组件Item.vue

<div>
   <van-switch v-model="itemVal"  />
</div>

<script>
props:['item', 'cell']

computed:{
   itemVal(){
     return this.cell.value1 ? this.item[this.cell.value][this.cell.value1] : this.item[this.cell.value]
   }
 },

</script>

于是,我们愉快的打开页面看下效果成功了没,发现控制台吐了块红色的错误。

Computed property "itemVal" was assigned to but it has no setter.

意思是:计算属性 itemVal被赋值了,但此它并未定义 set方法 。

要解决这个问题,首先要明确这个问题出现的原因。这个警告是由于Vue的计算属性内部没有set方法,即:计算属性不支持值的修改(只能针对data中的值进行计算)。

因为我们是v-model双向绑定,所以会触发到值的修改。

computed:{
   itemVal:{
     get(){
       return this.cell.value1 ? this.item[this.cell.value][this.cell.value1] : this.item[this.cell.value]
     },
     set(v){
       if(this.cell.value1){
         this.item[this.cell.value][this.cell.value1] = v
       }else{
         this.item[this.cell.value] = v
       }
     }
   }
 },

如上面所示,只要手动给计算属性添加get和set方法的不同操作,这个警告就解决了。

那既然可以写方法了,我们可以利用上面的_get方法去解析,就不需要写两个value、value1了,写一个value: "__config__.required",通过_get去解析即可。

后记

巧妙的利用计算属性可以是变量,来为v-model进行绑定。

这是在开发过程中的一些思路、一些尝试点。

代码总是一点一点优化,有时候先把功能点走通,再花点时间看看,能不能优化,精而再精🦀。

这也是在实际项目中真实遇到的场景。

来源:https://juejin.cn/post/7166786222419869732

标签:v-model,绑定,多循环表达式
0
投稿

猜你喜欢

  • PDO::beginTransaction讲解

    2023-06-06 00:57:46
  • Python爬虫基础之简单说一下scrapy的框架结构

    2022-01-04 23:19:00
  • CSS 设计中的黄金分割率应用

    2008-11-12 12:17:00
  • asp IsValidEmail 验证邮箱地址函数(email)

    2011-03-03 10:42:00
  • Python如何使用队列方式实现多线程爬虫

    2022-03-24 08:56:51
  • python线程安全及多进程多线程实现方法详解

    2023-08-27 02:01:54
  • python将Dataframe格式的数据写入opengauss数据库并查询

    2024-01-12 19:35:28
  • Mysql事务中Update是否会锁表?

    2024-01-14 19:11:33
  • Go语言 go程释放操作(退出/销毁)

    2023-09-17 22:03:42
  • 提升MySQL查询效率及查询速度优化的四个方法详析

    2024-01-14 21:05:11
  • pytorch教程resnet.py的实现文件源码分析

    2023-11-07 21:18:47
  • Python jieba库分词模式实例用法

    2023-12-09 23:40:42
  • Python实现手机号自动判断男女性别(实例解析)

    2021-06-24 21:47:28
  • Django migrate报错的解决方案

    2021-05-16 12:48:30
  • Centos7下mysql 8.0.15 安装配置图文教程

    2024-01-20 03:15:56
  • PHP 应用容器化以及部署方法

    2023-11-14 15:45:06
  • python使用fork实现守护进程的方法

    2021-08-27 00:37:50
  • python读取txt文件中特定位置字符的方法

    2022-07-02 17:38:52
  • 修改、删除数据记录(DELETE\\UPDATE)

    2009-02-27 15:50:00
  • Python切片工具pillow用法示例

    2021-12-08 21:17:26
  • asp之家 网络编程 m.aspxhome.com