ElementUI表单验证攻略:解决表单项启用和禁用验证的切换,以及动态表单验证的综合性问题
试想一种比较复杂的业务场景:
表格(el-table)的每一行数据的第一列是勾选框,最后一列是输入框。当某一行的勾选框勾上时,启用该行的输入框,并开启该行输入框的表单验证;取消该行的勾选框,则禁用该行的输入框,并禁用该行输入框的表单验证。
思路分析
动态表单验证
这里显然是一个数据遍历产生的动态表单验证问题,并且与el-table相结合。动态表单验证主要的难点在于表单项的prop属性的设置问题,由于是el-table中的表单,只需要使用scope.$index传递给prop,并将prop设置成形如"'productList.' + scope.$index + '.bidPrice'"的形式即可。具体可参见elementUI官网示例,这里不再赘述。
输入框启用和禁用切换
推荐的一个思路是,每当勾选项改变时,就设置每行数据的标志属性,如checked,并且每行输入框与该行数据的checked属性相帮定。每行数据原本并没有checked属性,即为undefined,因而输入框默认为禁用的。需要注意的是,我们需要使用$set来增加和修改该属性以实现响应式。当勾选项变化之时,根据勾选状态设置每行数据checked的值为true或false。
难点所在:输入框启用和禁用表单验证的切换
在上面的应用场景中,输入框是否启用验证是随着该行的勾选框状态切换而切换的,如果我们给el-input的上一层el-form-item设置固定的prop值,显然是不能达成目标的,无论启用或禁用,都会开启验证,难点在于如何让表单验证也能动态切换。
常见解决方案分析
1.给el-form-item的prop绑定一个计算属性。
问题在于:
由于prop需要随着切换而变化,因此计算属性也需要传递参数,但是计算属性一般是不传递参数
的,为解决参数问题,容易陷入“奇技淫巧”之中。个人认为,更关键的问题在于,prop在表单项
渲染的时候就已经确定,即便后来改了prop的值,由于表单没有重新渲染,因此不会生效。
2.给el-form-item的prop绑定一个函数,由函数计算prop值。
问题同样在于:
prop在表单项渲染的时候就已经确定,即便后来改了prop的值,由于表单没有重新渲染,
因此不会生效。
3.设置两个el-form-item,一个启用表单验证,一个不启用表单验证,两个基于v-if进行条件渲染,v-if绑定该行的checked属性。
该法理论上是可行的,并且思路清晰,比较简单。然而实际使用的时候却容易忽略Vue的复用机
制,出现没有重新渲染的问题,导致失败,进而怀疑v-if不能解决该问题。后面就会看到,本文
实际上就是使用了该法。
4.设置两个el-form-item,一个启用表单验证,一个不启用表单验证,两个基于v-show进行切换,v-show绑定该行的checked属性。
问题在于:
v-show不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。因而
即使只显示了不启用表单验证的el-form-item,另一个启用了表单验证的el-form-item的
验证也会生效,这种方案行不通。
5.自行遍历数据,进行全局提示,不使用elementUI的表单验证。
问题在于:
该方案较为复杂,需要根据数据进行大量的逻辑判断,实质上会比使用ElementUI的表单验证更为
复杂,并且只能进行全局消息提示,不能针对出错的某一行进行提示。
推荐解决方案: 基于v-if切换,但必须设置key属性
(一)方案简介
我们只需要写两个除了是否开启表单验证之外,完全相同的输入框表单项,使他们基于v-if进行条件渲染,并且切换时强制使他们重新渲染,就能实现业务需求。为了强制重新渲染,我们需要设置key属性,否则组件会复用,导致失败。
(二)条件渲染和复用
Vue官方文档指出,
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建
适当的含义在于:
- 若组件能够复用,比如v-if和v-else对应的组件几乎相同,只有极少数属性有差别,那么切换时,组件不会重新渲染,以实现复用。好处在于可以提高性能。
 - 若组件不能复用,比如v-if和v-else对应的组件差距太大,或者仅有v-if语句,或者强制使用key属性告诉vue v-if和v-else对应的组件是两个组件,不要复用;这样,当条件变化时,组件会重新渲染,包括事件也会重新绑定。
 
(三)key属性的作用
Vue为了尽可能高效地渲染元素以提高性能,通常会复用已有元素而不是从头开始渲染。这样也不总是符合实际需求,我们可能需要明确告诉Vue“这两个元素是完全独立的,不要复用它们”。具体方式是,只需添加一个具有唯一值的 key 属性即可。
- 由上可知,当我们不添加key属性的时候,由于Vue的复用机制,将使得v-if和v-else之间的元素可能不会重新渲染,如果要强制重新渲染,则必须使用key属性。
 
完整案例代码(可直接运行)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <style>
        .btn-container{
            margin-top: 40px;
            text-align: center;
        }
        .btn-container .confirm-btn{
            min-width: 70px;
        }
    </style>
</head>
<body>
    <div id="app">
        <el-form :model='formData' :rules='formRules' ref='bidForm'>
            <el-table :data='formData.productList' @selection-change='handleSelectChange'>
                <el-table-column type='selection'></el-table-column>
                <el-table-column label='名称' prop='name'></el-table-column>
                <el-table-column label='初始价格' prop='initPrice'></el-table-column>
                <el-table-column label='报价' width='180'>
                    <template slot-scope='scope'>
                        <el-form-item v-if='!scope.row.checked' key='noProp'>
                            <el-input size='small' v-model='scope.row.bidPrice' :disabled='!scope.row.checked' placeholder="<=50"></el-input>
                        </el-form-item>
                        <el-form-item :prop="'productList.' + scope.$index + '.bidPrice'" :rules='formRules.bidPrice' v-else key='hasProp'>
                            <el-input size='small' v-model='scope.row.bidPrice' :disabled='!scope.row.checked' placeholder="<=50"></el-input>
                        </el-form-item>
                    </template>
                </el-table-column>
            </el-table>
        </el-form>
        <div class="btn-container">
            <el-button type='primary' class="confirm-btn" @click='confirmBid'>确认投标</el-button>
        </div>
    </div>
    <script>
        var app = new Vue({
            el:'#app',
            data:{
                formData:{
                    productList:[{
                        id:0,
                        name:'土豆',
                        initPrice:3.2
                    },{
                        id:1,
                        name:'西红柿',
                        initPrice:4.2
                    }],
                },
                formRules:{
                    bidPrice:[{
                        validator(rule,value,callback){
                            if (value === '' || value === undefined) {
                                callback(new Error('请输入投标价格!'));
                            } else if (isNaN(value)) {
                                callback(new Error('请输入数字值!'));
                            } else if (Number(value) > 50 || Number(value) <= 0) {
                                callback(new Error('需大于0,小于等于50'));
                            } else if (/\.\d{4}/.test(value)) {
                                callback(new Error('最多带3位小数'));
                            } else {
                                callback();
                            }
                        },
                        trigger:'blur'
                    }]
                },
                selectArr:[]
            },
            methods:{
                // 选中项改变
                handleSelectChange(arr){
                    this.selectArr = arr;
                    // 给每项增加一个表示当前是否选中的标志属性
                    this.formData.productList.forEach(item => {
                        let checked = false;
                        arr.forEach(e => {
                            if(e.id === item.id){
                                checked = true;
                            };
                        });
                        if(checked){
                            this.$set(item,'checked',true);
                        }else{
                            this.$set(item,'checked',false);
                        }
                    });
                },
                // 点击确认
                confirmBid(){
                    this.$refs.bidForm.validate(valid => {
                        if(valid){
                            alert(111);
                        }
                    });
                }
            }
        });
    </script>
</body>
</html>
												
											ElementUI表单验证攻略:解决表单项启用和禁用验证的切换,以及动态表单验证的综合性问题的更多相关文章
- linux shell 脚本攻略学习15--如何只列出目录,如何快速切换目录
		
工作中经常遇到关于目录方面的问题,例如,如何只列出当前目录下的所有目录,以及如何快速高效的切换目录,而不需要使用鼠标,下面将简单介绍关于这两方面的解决方案: 一.如何只列出目录? 看似简单的任务,其实 ...
 - Angular 表单嵌套、动态表单
		
说明: 组件使用了ng-zorro (https://ng.ant.design/docs/introduce/zh) 第一类:嵌套表单 1. 静态表单嵌套 demo.component.html & ...
 - Oracle12c 性能优化攻略:攻略1-1:创建具有最优性能的数据库
		
一:章节前言 本章着眼于影响表中数据存储性能的数据库特性. 表的性能部分取决于在创建之前所应用的数据库特性.例如:在最初创建数据库时采用的物理存储特性以及相关的表空间都会在后来影响表的性能.类似地,表 ...
 - MYSQL 的静态表和动态表的区别, MYISAM 和 INNODB 的区别
		
MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的ISAM(Indexed Sequential Access Method:有索引的顺序访问方法)所改良.虽然性能极佳,但却有一个缺点 ...
 - 关于mysql存储过程创建动态表名及參数处理
		
转载请注明出处:帘卷西风的专栏(http://blog.csdn.net/ljxfblog) 近期游戏開始第二次内測,開始处理操作日志.最開始把日志放到同一个表里面,发现一天时间,平均100玩家 ...
 - 【翻译】Flink Table Api & SQL —Streaming 概念 ——动态表
		
本文翻译自官网:Flink Table Api & SQL 动态表 https://ci.apache.org/projects/flink/flink-docs-release-1.9/de ...
 - JQuery攻略(五)表单验证
		
表单验证,字段空白,输入合法,数据合法....... 此章节有 1.1字段验证 1.2正则表达式验证 1.3复选框的勾选 1.1字段验证 1.字段非空 $(document).ready(functi ...
 - ElementUI——动态表单验证
		
前言 版本更新迭代的时候,需要用到一个动态表单的功能,ElementUI刚好有教程就改改用咯 步骤 代码 <!-- 手机副号动态表单框 --> <el-form-item v-for ...
 - Oracle12c 性能优化攻略:攻略1-2:创建具有最优性能的表空间
		
问题描述: 1:表空间是存储数据库对象(例如索引 .表)的逻辑容器. 2:在创建数据库对象不为其指定存储属性,则相应的表和索引会自动继承表空间的存储特性. 故:若需要好的索引.表的性 ...
 
随机推荐
- Java运行时数据区域划分
			
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁时间.根据<Java虚拟机规范(Java SE 7版>的规定,J ...
 - 完整微信小程序授权登录页面教程
			
完整微信小程序授权登录页面教程 1.前言 微信官方对getUserInfo接口做了修改,授权窗口无法直接弹出,而取而代之是需要创建一个button,将其open-type属性绑定getUseInfo方 ...
 - .Net微服务实践(五)[服务发现]:Consul介绍和环境搭建
			
目录 介绍 服务发现 健康检查.键值存储和数据中心 架构 Consul模式 环境安装 HTTP API 和Command CLI 示例API介绍 最后 在上篇.Net微服务实践(四)[网关]:Ocel ...
 - 浅谈RPC与Http
			
什么是RPC,RPC原理是什么? 什么是RPC? RPC(Remote Procedure Call)远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.比如两个 ...
 - Hadoop(五):HDFS的JAVA API基本操作
			
HDFS的JAVA API操作 HDFS在生产应用中主要是客户端的开发,其核心步骤是从HDFS提供的api中构造一个HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS上的文件. 主 ...
 - P4015 运输问题【zkw费用流】
			
输入输出样例 输入 #1复制 2 3 220 280 170 120 210 77 39 105 150 186 122 输出 #1复制 48500 69140zuixiaofeiyo 说明/提示 1 ...
 - Java 方法 的使用
			
简单的说: 方法就是完成特定功能的代码块– 在很多语言里面都有函数的定义– 函数在Java中被称为方法 • 格式:– 修饰符 返回值类型 方法名(参数类型 参数名1, 参数类型参数名2…) {函数体; ...
 - Spring Cloud 系列之 Consul 注册中心(二)
			
本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Consul 注册中心(一) 本篇文章讲解 Consul 集群环境的搭建. Consul 集群 上图是一个简单的 Co ...
 - Linux忘记密码解决方案
			
Linux 忘记密码解决方法 很多朋友经常会忘记Linux系统的root密码,linux系统忘记root密码的情况该怎么办呢?重新安装系统吗?当然不用!进入单用户模式更改一下root密码即可. 步骤如 ...
 - developerWorks 中文社区
			
https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=3302cc3 ...