【问题解决】如何在 Vue <component> 切换子组件时优雅地进行 Form 表单校验

【问题解决】如何在 Vue <component> 切换子组件时优雅地进行 Form 表单校验

技术博客 admin 117 浏览

前言

在项目中使用 Vue <component> 遇到了一些挑战,特别是在需要对子组件中的表单进行校验时。问题在于,通过点击 <el-aside> 标签切换子组件时,并不能自动触发表单校验,这就需要在父组件中集成对子组件表单的校验逻辑。因此写下本篇博文记录这个问题并分享相关思考以及解决方法。

本篇博文所使用到的所有代码点击此处进行跳转

博文中的所有代码全部收集在博主的 GitHub 仓库中,相关技术栈专栏如下:

环境准备

  • Vue2;
  • element 2.15.14;

这里参照 官方文档 安装 Element,并在项目的 main.js 文件里进行导入:

js
复制代码
import Vue from 'vue' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import App from './App.vue' Vue.config.productionTip = false Vue.use(ElementUI); new Vue({ render: h => h(App), }).$mount('#app')

根据 官方文档 构建一个自定义化容器,效果如下所示:

构建容器代码如下所示:

html
复制代码
<template> <el-container style="height: 700px; border: 1px solid #eee"> <el-aside width="200px" style="background-color: rgb(238, 241, 246)"> <el-menu :default-openeds="['1']" @select="handleSelect"> <el-submenu index="1"> <template slot="title" ><i class="el-icon-message"></i>导航一 </template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-submenu> <el-menu-item index="2"> <template slot="title"><i class="el-icon-menu"></i>导航二</template> </el-menu-item> <el-menu-item index="3"> <template slot="title"> <i class="el-icon-setting"></i>导航三 </template> </el-menu-item> </el-menu> </el-aside> <el-container> <el-main> <ItemOne v-if="currentIndex === '1'"></ItemOne> <GroupOne v-else-if="currentIndex === '1-1'"></GroupOne> <GroupTwo v-else-if="currentIndex === '1-2'"></GroupTwo> <ItemTwo v-else-if="currentIndex === '2'"></ItemTwo> <ItemThree v-else-if="currentIndex === '3'"></ItemThree> </el-main> </el-container> </el-container> </template> <script> import GroupOne from "@/components/GroupOne.vue"; import GroupTwo from "@/components/GroupTwo.vue"; import ItemOne from "@/components/ItemOne.vue"; import ItemTwo from "@/components/ItemTwo.vue"; import ItemThree from "@/components/ItemThree.vue"; export default { name: "App", components: { GroupOne, GroupTwo, ItemOne, ItemTwo, ItemThree, }, data() { return { currentIndex: '1' }; }, methods: { handleSelect(index) { this.currentIndex = index } } }; </script>

子组件模板代码如下所示:

js
复制代码
<template> <p>导航一</p> </template> <script> export default { name: "ItemOne" } </script>

使用 <component> 标签优化代码

当构建容器组件时,我们通常希望根据不同的条件,动态地渲染不同的子组件。

js
复制代码
<el-container> <el-main> <ItemOne v-if="currentIndex === '1'"></ItemOne> <GroupOne v-else-if="currentIndex === '1-1'"></GroupOne> <GroupTwo v-else-if="currentIndex === '1-2'"></GroupTwo> <ItemTwo v-else-if="currentIndex === '2'"></ItemTwo> <ItemThree v-else-if="currentIndex === '3'"></ItemThree> </el-main> </el-container>

在上述代码中,使用了 if-else 结构来根据 currentIndex 的值选择不同的子组件进行展示。虽然这种方法可以实现功能,但随着子组件数量的增加,代码会变得冗长且难以维护。

为了优化这段代码,我们可以引入一个用于渲染动态组件或元素的 “元组件”:<component>,这是一个对象映射的方式,使代码更加简洁和易于管理,详细原理见官方文档

下面是详细的优化步骤:

  1. data() 中定义一个包含组件名称与对应索引关系的映射对象 componentMap,代码如下所示:
js
复制代码
data() { return { currentIndex: '1', componentMap: { '1': 'ItemOne', '1-1': 'GroupOne', '1-2': 'GroupTwo', '2': 'ItemTwo', '3': 'ItemThree' } }; },
  1. 然后,通过计算属性来返回当前需要渲染的子组件,在组件中添加计算属性 currentComponent,代码如下所示:
js
复制代码
computed:{ currentComponents() { return this.componentMap[this.currentIndex] } },
  1. 最后,在模板中使用这个计算属性来渲染子组件,代码如下所示:
html
复制代码
<el-container> <el-main> <component :is="currentComponents"></component> </el-main> </el-container>

单个组件表单验证

我们以 导航二 ItemTwo 为例,创建一个表单 Form,效果如下所示:

如果直接点击 “提交” 按钮,即使还有选项没有填写,表单也会被直接提交,效果如下所示:

因此我们需要进行表单验证,设置数据校验规则,在防止用户犯错的前提下,尽可能让用户更早地发现并纠正错误。

Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。校验规则参见 async-validator

下面是详细的验证步骤:

  1. 在 data() 中定义一个表单规则 rules,代码如下所示:
js
复制代码
data() { return { ..., rules: { name: [{ required: true, message: "请输入姓名", trigger: "blur" }], sex: [{ required: true, message: "请选择性别", trigger: "change" }], food: [ { required: true, message: "请选择喜欢吃的食物", trigger: "change" }, ], }, } }
  1. 接着在 Form 组件中传入验证规则,代码如下所示:
html
复制代码
<el-form ref="form" :model="form" :rules="rules"> <el-form-item label="姓名" prop="name"> ... </el-form-item> ... <el-form>
  1. 最后在提交表单时,进行数据的校验,代码如下所示:
js
复制代码
methods: { submitForm() { this.$refs.form.validate((valid) => { if (valid) { ... } }); }, },

最终效果如下所示:

【ItemTwo.vue】代码点击此处跳转

父组件调用子组件方法

在介绍父组件验证子组件表单之前,需要了解一个前置知识:父组件如何调用子组件的方法。

接下来我们以 App.vue 作为父组件,ItemThree.vue 作为子组件进行介绍父组件如何调用子组件的方法。

1、构建子组件页面,代码如下所示:

html
复制代码
<template> <div> <h1>导航三</h1> <p> 响应内容: <span style="color: #e86666">{{ msg }}</span> </p> </div> </template> <script> export default { name: "ItemThree", data() { return { msg: "当前没有响应内容!", }; }, }; </script>

效果如下所示:

2、编写一个子组件方法,可以更改 “响应内容”,代码如下所示:

js
复制代码
methods: { changeMsg(owner) { this.msg = `${owner} 组件改变了响应内容!` } }

效果如下所示:

3、父组件通过 ref 属性来调用子组件的方法。

  1. 向子组件添加 ref 属性,代码如下所示:
html
复制代码
<component :is="currentComponents" ref="child"></component>
  1. 编写父组件调用子组件的方法,代码如下所示:
js
复制代码
handleClick() { this.$refs.child.changeMsg("Parent"); },

效果如下所示:

父组件校验子组件表单

1、在子组件中创建一个校验方法 handleValidForm(),代码如下所示:

js
复制代码
handleValidForm() { let flag = false this.$refs.form.validate((valid) => { if (valid) { flag = true this.submitForm() } }); return flag },

上述代码先对表单进行校验,若校验通过则触发表单提交,并返回一个标识位 flag,用于标识表单校验是否通过。

2、在父组件中调用此方法,通过获取到的标识位 flag 来判断表单是否校验通过,代码如下所示:

js
复制代码
handleSelect(index) { if (this.$refs.child.handleValidForm()) this.currentIndex = index; }

上述代码表示如果校验通过,则实现子组件的切换,否则不做任何操作。

需要注意的是,每个被 <componet> 所使用的子组件都需要具有 handleValidForm() 方法,否则会出现报错:

js
复制代码
vue.runtime.esm.js:4427 [Vue warn]: Error in v-on handler: "TypeError: this.$refs.child.handleValidForm is not a function"

3、效果如下所示:

【整体】代码点击此处跳转

后记

以上就是 如何在 Vue <component> 切换子组件时优雅地进行 Form 表单校验 的所有内容了,希望本篇博文对大家有所帮助!欢迎大家持续关注我的博客,一起分享学习和成长的乐趣!✨

📝 上篇精讲:解决 ECharts 图表窗口自适应与数据不渲染问题

💖 我是 𝓼𝓲𝓭𝓲𝓸𝓽,期待你的关注,创作不易,请多多支持;

👍 公众号:sidiot的技术驿站

🔥 系列专栏:问题解决前端大杂烩Vue.js 打怪升级之路

源文:【问题解决】如何在 Vue 切换子组件时优雅地进行 Form 表单校验

如有侵权请联系站点删除!

技术合作服务热线,欢迎来电咨询!