在 Vue 中多表单深层次嵌套结构,创建响应式属性,以及多层级表单验证

设置响应式属性

在 Vue 中,只有在跟级别中创建的属性,才具有响应式,如果后期手动添加,如使用 this.xxx = xxx这种方式创建的属性是不具有响应式的。
在 Vue里面,封装了 7 个方法,使用如下的方法来添加属性,同样可以创建响应式属性,并触发视图更新:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

如果不使用以上的方法,Vue 也提供了$set方法来设置响应式属性,使用方式为this.$set(obj, newObj, value),在 Vue 的单文件组件里,大概是这样写的:

<script>
export default {
	data() {
		return {
			obj: {}
		}
	},
	
	methods: {
		setValue() {
			this.$set(this.obj, 'name', 'tim')
		}
	}
}
</script>

以上的代码表示,在 obj 里面,添加一个名为 name 值为 value的属性。添加完之后,data 里面应该是这样的:

data() {
	return {
		obj: {
			name: 'tim'
		}
	}
}

为了验证一下有效性,我们新建一个组件,ui 框架选用 element,来测试一下到底是否可行:

<template>
    <div class="test">
        <el-form ref="form" :model="form" label-width="80px">
            <el-form-item label="name">
                <el-input v-model="form.name"></el-input>
            </el-form-item>
        </el-form>

        <el-button type="primary" @click="testFun">test</el-button>
    </div>
</template>

<script>
export default {
    name: 'test',
    data() {
        return {
            form: {
                
            }
        }
    },
    methods: {
        testFun() {
            this.form.name = 'tim'
        }
    }
}
</script>

<style scoped></style>

先点击 test 按钮,我们往 form 里面添加了 name 属性,然后在表单输入值,发现输入不进去,打开 devtools 发现值也没有改变。

我们把 testFun 函数改写一下:

testFun() {
    this.$set(this.form, 'name' 'tim')
}

这回在测试一下,先点击 test 按钮,然后发现表单被赋上值了,然后修改表单同样能够更新值。

以上表示,如果我们没有一开始在 data 里面创建跟级别的属性,需要使用 $set 来创建。


不过我们要说的不止是这个,而是创建多层嵌套的二维三维数组和对象的响应式,并触发视图更新。实际应用主要在表单的多层嵌套上,需要复原数据的情况下

现在假设我们需要复原的数据为如下格式:

let copyData = [
    [
        {
            name: 'tim',
            age: 10
        },
        {
            name: 'tim',
            age: 20
        }
    ],
    [
        {
            name: 'andy',
            age: 15
        },
        {
            name: 'andy',
            age: 30
        },
    ]
]

这里面,既有数组又有对象,基本能把创建多维数据的响应式属性讲清楚。对于创建响应式数组和对象的原理,可以点击这里查看官网的介绍(建议看一眼之后,再来看这个例子),在此只提供实例进行讲解。

  1. 首先,我们先创建一个初始化页面,一般来说,我们不可能创建一个空的表单 data,所以需要填入属性,只不过没有值而已。
<template>
  <div class="test">
    <el-form ref="form" :model="form" label-width="80px">
      <div class="parent" v-for="(parent, index) in form.list" :key="`parent-${index}`">
        <div class="child" v-for="(child, idx) in parent" :key="`child-${idx}`">
          <el-form-item label="name">
            <el-input v-model="child.name"></el-input>
          </el-form-item>
          <el-form-item label="age">
            <el-input v-model="child.age"></el-input>
          </el-form-item>
        </div>
      </div>
    </el-form>

    <el-button type="primary" @click="testFun">test</el-button>
  </div>
</template>

<script>
export default {
  name: "test",
  data() {
    return {
      form: {
        list: [
          [
            {
              name: "",
              age: ""
            }
          ]
        ]
      }
    };
  },
};
</script>

<style scoped></style>	

以上是完整的一个单 Vue 文件。主要是初始化了一个嵌套的多层级表单。
在这里插入图片描述
在这里插入图片描述

如上,我们在表单里修改内容,在 devtools 里面,可以看到值能够更新
2. 有了以上的布局结构,我们就可以模拟数据了。

let copyData = {
  list: [
    [
      {
        name: "tim",
        age: 10
      },
      {
        name: "tim",
        age: 20
      }
    ],
    [
      {
        name: "andy",
        age: 15
      },
      {
        name: "andy",
        age: 30
      }
    ]
  ]
};

假设我们通过接口获取到如上数据,然后我们需要把如上的数据写入到页面里面,那么我们直接通过 this.form = copyData 只有第一层级的基础数据结构有效果(Boolean、String、Number),其他是没有效果的(Array,Object),也就是说,在多层级嵌套的情况下,我们需要通过 Vue 提供的上述 7 种方法,或者 $set 来设置响应式属性。

  1. 这次我们使用 $set 来完成响应式属性的设置。

    首先我们看 copyData 里面的 list,是一个数组,所以我们需要使用 $set 来完成对数组的操作。

    你也可以使用 vm. s e t 实 例 方 法 , 该 方 法 是 全 局 方 法 V u e . s e t 的 一 个 别 名 : v m . set 实例方法,该方法是全局方法 Vue.set 的一个别名: vm. setVue.set:vm.set(vm.items, indexOfItem, newValue)

    上面的vm.items其实就是要设置的对象,indexOfItem我们可以通过遍历 list 来获取 indexnewValue 就是对应的值。

    copyData.list.forEach((parent, index) => {
       this.$set(this.form.list, index, copyData.list[index]);
     });
    

通过以上代码,我们点击 test按钮,发现页面会触发更新,同时渲染出对应的的内容。表明我们设置成功了,如果还有在深层次的嵌套,同样需要依次遍历对应的值,然后使用$set设置即可。

表单验证

对于 element-ui 的表单验证,我们需要在 html 上添加一些属性

<div class="test">
    <el-form ref="form" :model="form" label-width="80px" :rules="rules">
      <div class="parent" v-for="(parent, index) in form.list" :key="`parent-${index}`">
        <div class="child" v-for="(child, idx) in parent" :key="`child-${idx}`">
          <el-form-item label="name" prop="name">
            <el-input v-model="child.name"></el-input>
          </el-form-item>
          <el-form-item label="age">
            <el-input v-model="child.age"></el-input>
          </el-form-item>
        </div>
      </div>
    </el-form>
    <el-button type="primary" @click="testFun">test</el-button>
  </div>

// ...script
// data
rules: {
   name: {
     required: true,
     message: "请输入值",
     trigger: "blur"
   }
 }
  1. 第一步,首先在 el-form上,添加 :rules="xxx",xxx 可以随便设置,这里我设置为 rules
  2. 第二部,在要验证的表单item 上,添加prop,这个 prop 对应 rules 里面的值,这里设置为 name

其实到这里已经设置完了,我们发现,在只有一个 name 表单的情况下,能够正常验证(没有通过 v-for 遍历),但是如果是多个表单同时都指向 name 呢 ?

所以我们可以在单个的表单域上传递属性的验证规则:

<el-form-item
    label="name"
     :prop="`list.${index}.${idx}.name`"
     :rules="{ required: true, message: '请输入邮箱地址', trigger: 'blur' }">
  <el-input v-model="child.name"></el-input>
</el-form-item>

rules 上,我们设置为单独的 rules,而 prop比较麻烦,需要以 index. 的方式来取对应的字段,这里需要注意。

那么通过以上设置后,我们就可以对每个 name 字段分别进行验证了。

Logo

前往低代码交流专区

更多推荐