项目中的 Vue 需要使用表单验证,在网上搜了一些资料,看来看去,还是 VeeValidate 貌似好用点,至少 github 排行最高。

此外 Vue 官网的 Cookbook,其中的 "表单校验" 最后的 "其他替代模式" 也提到了 VeeValidate。

打开官网一看,完全就是 Vue 官网的风格,让人都怀疑是不是 Vue 官方出品的,而且大致浏览了下,文档写的非常好!

实在是找不到中文版的,打算简单翻译下,顺带熟悉下怎么使用!

本篇总结 Guide

Introduction
	VeeValidate 是专用于 Vue.js 的验证库。它有很多开箱即用的验证规则,也支持自定义验证规则。它基于模板,所以它和 HTML5 的验证 API 比较类似,而且我们也比较熟悉。我们可以验证 HTML5 input 输入框,以及我们自定义的 Vue 组件。

	它也是以本地化为基础构建的,事实上,它支持大约 44 种语言,由社区成员来支持和维护。

	特点:
		基于模板的验证
		提供了许多开箱即用的验证规则
		一流的本地化支持
		可以验证 HTML5 input 输入框和自定义 Vue 组件
		自定义规则和错误消息

Getting Started
	安装:
		可以通过 npm 或 CDN 来安装该插件

		npm:
			npm install vee-validate --save

		CDN:
			2 个国外的 CDN,国内估计危险
			<script src="https://cdn.jsdelivr.net/npm/vee-validate@latest/dist/vee-validate.js"></script>
			<script src="https://cdn.jsdelivr.net/npm/vee-validate@latest/dist/vee-validate.js"></script>

	使用:
		提示:
			示例使用 ES2015 语法,如果您还没有,请确保在 ES2015 上使用

		import Vue form 'vue';
		import VeeValidate form 'vee-validate'
		Vue.use(VeeValidate);

		或者直接通过 <script> 引入

		<script src="path/to/vue.js"></script>
		<script src="path/to/vee-validate.js"></script>
		<script>
			Vue.use(VeeValidate);
		</script>

	基础示例:
		我们需要做的就是将 'v-validate' 指令添加到我们想要验证的 input 输入框上,然后确保 input 输入框有一个 name 属性,用来生成错误消息

		之后,给指令传入一个 'rules' 字符串,它包含一系列的,使用 '|' 管道符分隔的验证规则。对于下面的示例,验证规则非常简单。使用 'require' 来表示字段必填,'email' 来表示字段必须是一个邮箱。为了将这两个规则组合起来,我们将字符串值 'required|email' 作为 'v-validate' 表达式的值。

			<input v-validate="'required|email'" name="email" type="text">

		为了显示错误消息,我们简单地使用 'errors.first' 方法,来获取该字段生成的第一个错误:

			<span>{{ errors.first('email') }}</span>

		警告:
			客户端验证永远不能替代服务器端验证。确保在后端验证用户的任何输入。

Syntax
	VeeValidate 的验证规则具有类似于 'Laravel 验证' 的简单语法。(正好我们项目后端就使用的 Laravel)

	验证表达式,是由一系列的,使用 '|' 分隔的验证器组成的字符串:

		const single = 'required';		// 单个规则
		const multiple = 'required|numeric';	// 多个规则

	规则表达式,也可以是一个复杂的、可读性更好的规则对象:

		const single = { required: true };
		const multiple = {
			required: true,
			numeric: true,
			email: true,
		};

	规则参数:
		一些规则可以有参数,为方便起见,可以通过多种方式指定参数:
			1>逗号分隔的参数列表,适用于字符串格式

				const someRule = 'included:1,2,3,4';

			2>一个包含参数值的数组(适用于对象格式)

				const someRuleObj = { included: [1, 2, 3, 4] };

			3>一个对象(针对对象格式的规则,提供了更复杂的配置),但需要注意

				const someCplxObj = {
					email: {
						allow_uft8_local_part: true
					}	
				};
		提示:
			在对象格式中,如果规则接收单个参数,则规则接收单个值。如果传递了多个参数,则应以同样的顺序将它们整合为一个数组传入。

	规则实践:
		把我们学到的东西付诸实践,让我们用两种表达形式创建我们的字段,并遵循以下规范:

		一个必填的 email 字段:

			<input v-validate="'required|email'" type="email" name="email">
			<input v-validate="{ required: true, email: true }" type="email" name="email">

		一个非必填的 username 字段:

			<input v-validate="'alpha'" type="text" name="username">
			<input v-validate="{ alpha: true }" type="text" name="username">

		一个必填的 password 字段,且最小长度为 6 个字符:

			<input v-validate="'required|min:6'" type="password" name="password">
			<input v-validate="{ required: true, min: 6 }" type="password" name="password">

		提示:
			字符串表达式两边有 "'"(单引号)。因为 Vue 指令会计算给定表达式的值,由于我们想要将它求值为一个字符串,所以需要用 "'"(单引号) 将它括起来,这意味着 v-validate="required" 将失败,因为 v-validate 将尝试在 Vue 实例上查找一个名为 'required' 的 prop 或 method,而这很可能不存在,所以会失败。

Validation Rules
	VeeValidate 有一堆开箱即用的验证规则,它们都是本地化的,涵盖了大多数的验证需求.

	具体规则,看文档!!!

Custom Rules
	我们可以轻松地向 VeeValidate 添加自定义规则,但是我们的自定义规则必须遵循一个 constract 或特定的结构:

	创建一个自定义规则:
		函数形式
			这是最基本的自定义验证器方式。它仅由一个函数组成,该函数返回一个布尔值或Promise。但是,它将有一个默认的错误消息。

				const validator = (value, args) => {
					// 返回一个布尔值或一个可以解析为布尔值的 Promise 实例
				};

		对象形式
			const validator = {
				getMessage(field, args){
					// 将被添加到默认的语言环境消息中
					// 返回一个消息
				},
				validate(value, args){
					// 返回一个布尔值或一个可以解析为布尔值的 Promise 实例
				}
			};

			这个验证器对象必须有一个 validate 方法,并且可以包含一个 getMessage 方法,该方法将会合并到当前的字典语言环境中。对于多语言,我们应该使用 localization API。

			提示:
				注意 getMessage 方法如何接收 'field','field' 是要验证的字段名作为第一个参数,以及 validate 方法如何接收 value 作为第一个参数。两个方法都接收一个 'args' 数组,该 'args' 数组包含了验证规则配置的参数。

			警告:
				正如我们所看到的,验证规则必须实现上述两种形式之一。不这样做将会抛出一个对应的错误消息的异常,详细说明我们缺少的内容。

	使用自定义规则
		创建了自定义规则后,我们可以使用验证器实例的 extend(name, validator) 方法,将其添加到规则列表。

			import {Validator} from 'vee-validate';

			// 这个是 '类' 的 extend() 方法
			Validator.extend('truthy', {
				getMessage: field => 'The ' + field + ' value is not truthy',
				validate: value => !!value
			});

			let instance = new Validator({ trueField: 'truthy' });

			// 这个是 '实例' 的 extend() 方法
			// 为方便起见,在实例上也有一个 'extend' 方法
			instance.extend('falsy', (value) => ! value);

			instance.attach({
				name: 'falseField',
				rules: 'falsy'
			});

		提示:
			使用类或实例上的 extend 方法,都会使用新的验证规则扩展所有验证器。扩展一个 '与现有规则同名的' 新规则,将以静默方式覆盖它

		之后,我们就可以像其他规则一样使用新规则:

			<input type="text" name="field" v-validate="'falsy'">

		警告:
			当验证的字段 '非必填'(not required),可能根本不会执行我们的规则。这是因为 VeeValidate 对于 '非必填' 的 '空字段' 会跳过验证。

		参数和规则配置
			我们的规则可能提供不同的结果/行为,取决于我们的某些配置,正如之前提到的,它们作为 '值数组'(an array of values) 传递给验证器函数,这可能并非总是最好的,例如,一个具有3个可选参数的规则,仅仅为了指定第三个参数,每次都需要接收所有的3个参数。

			通过在扩展选项(第3个参数)中提供一个 'paramNames' 数组,可以令我们的验证规则接收一个对象,而不是数组。该对象命名了我们通常接收的数组内的变量。

			举个例子来看一个基本的 'value between' 规则实现:

				const isBetween = (value, { min, max } = {}) => {
					return Number(min) <= value && Number(max) >= value;
				};

				// 第一个参数称为 'min',第二个参数称为 'max'
				const paramNames = ['min', 'max'];

				Validator.extend('between', isBetween, {
					paramNames 		// 在扩展选项中传递它
				});

			paramNames 并非真的必须,但是它允许我们的规则,以 sting/object 两种格式来工作。因为字符串格式无法传递命名参数。

				between: 10,20

			在这种情况下,顺序控制着参数名称(10 对应着 min;20 对应着 max,所以说顺序决定了参数名)。验证器将使用 paramNames 将字符串数组转换为一个对象,该对象以命名的参数作为键,对应的正确的参数值作为值。确保以 '与字符串格式指定的参数的相同的顺序' 来排序 paramNames。

			警告:
				Locale 方法仍将接收 args 数组,该方法不会将 args 转换为对象,因为如果修改了可能会导致 locale 文件需要重构。

		目标依赖规则
			有时候我们的规则可能需要将验证的字段值,同另一个字段值进行比较,一些内置的规则,像:confirmed、before 和 after,就需要一个目标字段来比较。

			通过在扩展选项对象(第三个参数)上设置 hasTarget 属性,我们也可以创建自定义规则来实现 '目标依赖规则'。

				Validator.extend('isBigger', (value, [otherValue]) => {
					return value >= otherValue;
				}, {
					hasTarget: true
				});

			注意,otherValue(其他字段值) 将作为参数列表中第一项被注入。

			这些规则至少需要一个参数,并且目标字段必须有一个匹配的 ref 值。
			PS:
				最少一个参数,就是指 '目标字段',同时必须存在一个 ref 为 '目标字段' 的 input 输入框

				<input v-validate="'confirmed:confirmation'" name="password" type="password">
				<input name="passwordConfirmation" ref="confirmation" type="password" placeholder="确认密码">
				(目标字段 confirmation,必须对应着一个 ref 为 confirmation 的 input 输入框。<input ref="confirmation" ...>)

		非立即执行规则
			无论我们是否使用 imediate 修饰符,VeeValidate 都会触发初始验证,不同之处在于,如果设置了 imediate 修饰符,会更新错误和标志。

			有时候我们不希望执行规则,试想一个调用远程 API 的规则。除非设置了 imediate 修饰符,否则我们不希望执行该规则。这可以通过向扩展选项中添加 'imediate' 布尔值来实现。
				Validation.extend('remote', (value, [otherValue]) => {

				}, {
					imediate: false
				});

			当设置 imediate 为 false,会在初始验证时,跳过该规则。

		原因
			此外,我们可能希望提供一个 '可以更改错误消息' 的验证失败的原因。例如,我们正在使用一个外部 API,并且该 API 提供了错误消息。 

			为了实现此目的,我们的验证器函数应该返回一个 Object 而非 Boolean。该对象应始终包含一个 valid 属性和一个可选的 data 属性。data 属性将作为第三个参数传递给消息生成器函数,之后,我们应该使用被传入的 data 属性来修改输出消息。这这些同样适用于 promise,我们可以使用包含这些属性的对象来解析 promise。下面是一个实现此目的的自定义规则:

				const myRule = {
					getMessage(field, params, data){
						return (data && data.message) || '出错了';	
					},
					validate(value){
						return new Promise(resolve => {
							resolve({
								valid: value === 'trigger' ? false : !!value,
								data: value !== 'trigger' ? undefined : { message: 'Not this value' }
							});
						});
					}
				};

Inferred Rules
	虽然我们可以在 v-validate 指令中指定规则,但是 vee-validate 还会根据 input 输入框类型,分析推断其他规则。例如,如果我们有以下内容:

		<input
			name="email"
			type="email"
			required
		>

	指定 v-validate="'required|email'" 将是多余的。因为 vee-validate 将检测 input 输入框的类型,以及 required 属性,并且会自动为我们包含这些规则,因此我们只需要在 input 输入框上添加 v-validate 即可。

		<input
			name="email"
			type="email"
			required
			v-validate
		>

	推断规则参考:
		这是一个 HTML 属性表,这些属性会被推断为规则。

		属性名 - 值 - 规则
		type - email - email
		type - number - decimal
		type - date - date_format:YYYY-MM-DD
		type - datetime-local - date_format:YYYY-MM-DDThh:mm
		type - time - date_format:hh:mm 或 date_format:hh:mm:ss 取决于 time 输入框步长
		type - week - date_format:YYYY-Www
		type - month - date_format:YYYY-MM
		min - val - min_value:val
		max - val - max_value:val
		pattern - rgx - regex:rgx
		required - none - required

	提示:
		此功能不适用于自定义组件,只有 HTML5 input 输入框才可以利用此功能

Error Messages
	VeeValidate 附带了一般的错误消息,这些错误消息可以被覆盖,或者为特定字段分配特定消息。

	消息生成器
		消息存在于内部字典中。它们可以是字符串,也可以是返回字符串的函数,称为 generators。这些生成器有以下结构:

			function rule (fieldName: string, params: any[], data?: any): string {
				return `Some error message for the ${fieldName} field`;
			}

		它接收字段名或其别名作为第一个参数(fieldName),以及用于验证该字段的参数(params)。第三个可选参数是验证规则返回的任意其他数据,可以为生成器提供更多信息,让其更加灵活。(data)

	覆盖消息
		Validator 类和它的实例提供了一个 localize 方法,该方法将提供的消息与内部字典合并,覆盖任何重复项。

		提示:
			由于消息字典是共享的,任何合并都将对所有验证器实例产生影响。

			import { Validator } from 'vee-validate';

			const dictionary = {
			  	en: {
			    	messages:{
				      	alpha: () => 'Some English Message'
			    	}
			  	},
			  	ar: {
			    	messages: {
			  	    	alpha: 'حاجة عربي'
			    	}
			  	}
			};

			// 覆盖并合并字典
			Validator.localize(dictionary);

			const validator = new Validator({ first_name: 'alpha' });

			validator.localize('ar'); // 现在该验证器将生成 Arabic 语言的消息

		警告:
			必须以 '对象路径' 的方式提供消息,类似 'dictionary.locale.messages'。

		通常,我们会为应用程序构建我们的语言文件,而不是像上面的示例那样进行硬编码。查看本地化指南获取更多信息(https://baianat.github.io/vee-validate/guide/localization.html)。默认情况下,
		没有指定规则的错误消息,会自动回退使用系统默认的错误消息,我们只需要定义我们需要自定义的消息。

	字段名
		与自定义消息一样,验证器共享一个包含属性名的字典。例如,我们想在错误消息中,使用 'Email Address' 替代 'email',可以通过在字典中包含一个 attributes 对象来轻松实现。

		与消息不同的是,在默认的字典中,没有包含任何属性。

			import { Validator } from 'vee-validate';
			const dictionary = {
			  	en: {
			    	attributes: {
				      	email: 'Email Address'
			    	}
			  	},
			  	ar: {
			    	attributes: {
				      	email: 'البريد الاليكتروني'
			    	}
			  	}
			};

			Validator.localize(dictionary);

		提示:
			如果在当前语言环境找不到该属性,会回退使用绑定的表达式或字段名。如果我们使用了 data-vv-as 属性,它会优先于内部字典。

	指定字段的自定义消息
		我们可能想要给不同字段提供不同消息。例如,我们可能希望当 email 字段是 required 时,显示一个错误消息,但是当 name 字段是 required 时,显示一个不同的消息。这允许我们为用户提供灵活的体验和上下文感知消息。

		为此,我们需要给字典添加一个叫做 custom 的对象,如下:

			const dist = {
				custom: {
					email: {
						required: 'Your email is empty'
					},
					name: {
						required: () => 'Your name is empty'
					}
				}	
			};

			Validator.localize('en', dict);
			// 或使用实例方法
			this.$validator.localize('en', dict);

		提示:
			要记住的一件事是,在我们代码中替换任何字典的相关操作,一定要在它真正使用之前,以避免不必要的合并。例如,一个非常好的常见位置是,在我们应用的入口或启动脚本。相反,一个糟糕的选择是,像 mounted 这样的子组件的生命周期钩子,因为验证器字典对所有实例是全局共享的。

Localization
	插件默认只附带 English 消息,来保持小巧,但是它架构时考虑了灵活的消息生成机制。'English 消息文件'(https://github.com/baianat/vee-validate/blob/master/locale/en.js) 是一个关于如何构造这些消息的示例。之后,我们可能希望更新验证器字典,这应该只在我们应用程序启动时发生一次。不过,我们可以随时在我们应用程序的任何位置更新他们。查看下面的字典章节。

	别名
		我们不仅可以创建和覆盖消息,还可以为消息中使用的字段提供别名,来代替原始名称。在错误消息中看到 'first_name',对用户体验来说可能不是很好。有两种解决方案。

		使用 data-vv-as
			我们可以在字段上使用 data-vv-as,像这样:

				<input v-validate="'alpha'" data-vv-as="First Name" name="first_name" type="text">

			现在,当上面 input 输入框生成任何错误消息时,它将使用 data-vv-as 属性的值来替代真实的字段名。虽然这对于简单设置和显示本地化名称非常有用,但是仅适用于单一语言环境页面。对于多语言环境页面和更高级的用法,我们可能需要使用 '字典 API'。

		使用字典 API
			所有验证器都可以访问一个它们之间共享的简单字典。该字典包含本地化的错误消息和属性。如果验证器为某个字段找到了本地化的属性名,将使用它来代替字段名,非常类似 data-vv-as,但是如果同时使用了 '字典API' 和 data-vv-as,data-vv-as 具有更高的优先级。

			下面是一个 '关于如何添加对本地化消息和属性的支持' 的代码示例。

				import Vue from 'vue';
				import VeeValidate from 'vee-validate';
				import messagesAr from './strings/validator/messages/ar.js';
				import attributesAr from './strings/validator/attributes/ar.js';
				import attributesEn from './strings/validator/attributes/en.js';

				// 传递选项,使所有验证器使用阿拉伯语言,并且将英语和阿拉伯语属性与内部字典合并
				Vue.use(VeeValidate, {
				  	locale: 'ar',
				  	dictionary: {
				    	en: { attributes: attributesEn },
				    	ar: { messages: messagesAr, attributes: attributesAr }
				  	}
				});

				new Vue({
				  	el: '#app',
				  	data: {
				    	// Some Data ...
				  	},
				  	methods {
				    	// Cool methods ...
				  	}
				});

			警告:
				本地化逻辑应该发生在我们的应用程序入口,因为它应该只执行一次。

	本地化 API
		验证器类提供了一个静态的 localize 方法,该方法在所有实例上也可用,有 3 种使用方式:

			const dictionary = {
				en: {
					// 属性和消息
				},
				ar: {
					// 属性和消息
				}
			}

			// 切换本地语言
			Validator.localize('en');

			// 合并 dictionary 中的英语,并设置为当前本地语言为英语
			Validator.localize('en', dictionary.en);

			// 合并定义在 dictionary 中的所有语言,但是不设置为当前本地语言
			Validator.localize(dictionary);

		所有验证器的语言环境都是共享的。在任何组件中的任何可用实例上,调用 localize 都将更改所有验证器的语言设置。

			// 在组件中
			this.$validator.localize('ar');

			import { Validator } from 'vee-validate';

			// 在原型也可用
			Validator.localize('ar');

		如果将语言环境设置为一个尚未合并到字典中的语言环境,会收到警告。生成的任何消息都将回退到英语(默认的语言包)。

	本地化文件	
		在插件的 locale 目录,是本地化文件的集合。如果找不到我们需要的语言环境,我们可以向官方代码库贡献本地化版本,帮助改善插件。

		我们可以像下面这样导入这些语言环境:

			import ar from 'vee-validate/dist/locale/ar';
			import VeeValidate, { Validator } from 'vee-validate';

			// 安装插件
			Vue.use(VeeValidate);

			// localize 方法接收 locale 对象作为第二个参数(可选的),并合并它
			Validator.localize('ar', ar);

		我们必须注意,本地化文件需要导出如下的对象结构:

			export default {
				name: '{locale}',
				messages: {
					...	
				}
			}

		也要注意,如果通过 <script> 标签引入,且 VeeValidate 是全局可用的,他们将被自动安装。

	异步本地化
		加载应用包中的每个语言环境,有点浪费用户带宽,尤其是当只使用一个语言环境,不需要加载全部的。通过 Webpack import(),我们可以轻松进行异步本地化设置:

			import Vue from 'vue';
			import { Validator } from 'vee-validate';	

			Vue.mixin({
				localize(localeName){
					
					// 在这里本地化我们的应用程序,例如,i18n 插件	
					// 异步加载本地化文件,之后就可以使用它来进行本地化验证
					import(`./path/to/vee-validate-locales/${localeName}`).then(locale => {
						Validator.localize(localeName, locale);
					});
				}	
			});

	VueI18n 集成
		VeeValidate 支持 vue-i18n 插件,考虑到它是 Vue.js 本地化解决方案中最受欢迎的。我们可以通过将 i18n 实例传递给 vee-validate 配置,把 VueI18n 集成到 vee-validate:

			import VeeValidate from 'vee-validate';
			import Vue from 'vue';
			import VueI18n from 'vue-i18n';
			import validationMessages from 'vee-validate/dist/locale/en';

			Vue.use(VueI18n);

			const i18n = new VueI18n();

			Vue.use(VeeValidate, {

				// 自定义验证消息的 root 路径
				i18nRootkey: 'validations',
				i18n,
				dictionary: {
					en: validationMessages
				}
			});

		当我们将字典传递给配置时,它会与我们的 i18n 语言环境消息合并。安装插件后,我们应该只使用 i18n 实例设置语言环境,这意味着:

			// 失败并返回一个警告
			this.$validator.locale = 'ar';

			// 成功并生成错误消息
			this.$i18n.locale = 'ar';

		警告:
			当使用 vee-validate 和 i18n 时,可能会遇到警告,这是因为 vee-validate 为 i18n 插件留下了回退机制,我们可以安全地忽略这些警告。我们可以通过设置 i18n 的 silentTranslationWarn(https://kazupon.github.io/vue-i18n/api/#silenttranslationwarn) 配置静默这些警告(silence them)。

	自定义 i18n 驱动
		我们的应用程序可能已经使用了一个不同的 i18n 系统,vee-validate 内部使用驱动器生成其消息。为了避免不得不维护两个不同的本地化驱动,我们可以轻松地将自定义驱动集成到 vee-validate。

		我们需要创建一个实现了 'IDictionary 接口' 的对象。并在安装了插件后,可以将新驱动应用到 vee-validate。

			import Vue from 'vue';
			import VeeValidate from 'vee-validate';

			Vue.use(VeeValidate);

			const dictionary = {
			  // 我们的实现
			};

			VeeValidate.setI18nDriver('custom', dictionary);

Flags
	介绍
		vee-validate 包含了一些可以帮助我们改善用户体验的标志,每个验证字段都有自己的一组标志,它们是:

			touched - 表示该字段已经被 touched 或 focused
			untouched - 表示该字段没有被 touched 或 focused
			dirty - 表示该字段已经被操作过
			pristine - 表示该字段没有被操作过
			valid - 表示该字段已经通过验证
			invalid - 表示该字段验证失败
			pendding - 表示该字段正在验证中
			validated - 表示该字段已经通过事件或手动调用了 validate() 或 validateAll() 方法,被验证过至少一次
			changed - 表示该字段的值已经被改变过(严格检查 - 应该是类型改变也算)

		这些表示是响应式对象,所以我们可以基于它们来构造计算属性(vue 的 computeds)。例如,以下是如何判断表单是否被操作过,可能是 '禁用/启用' 一个按钮

			export default {
				computed: {
					isFormDirty(){
						return Object.keys(this.fields).some(key => this.fields[key].dirty);
					}
				}
			}

		可以通过如下对象访问全局字段标志:

			// 'name' 字段是否已经被污染(操作过)
			this.fields.name.dirty;

		然而,对于作用域字段,FieldBag 会将这些字段以 '$' 为前缀的属性名来分组,表示它是一个作用域对象:

			// 'name' 字段是否已经被污染(dirty)
			this.fields.$myScope.name.dirty;

			// 'name' 字段是否没有被污染(clearn 或 pristine)
			this.fields.$myScope.name.pristine;

		下面是它的例子:

			<div class="form-input">
				<input type="text" name="email" v-validate="'required|email'" placeholder="Email">
				<span v-show="errors.has('field')">{{ errors.first('field') }}</span>
				<span v-show="fields.email && fields.email.dirty">I'm Dirty</span>
				<span v-show="fields.email && fields.email.touched">I'm touched</span>
				<span v-show="fields.email && fields.email.valid">I'm valid</span>
			</div>

			<div class="form-input">
				<input data-vv-scope="scope" type="text" name="email" v-validate="'required|email'" placeholder="Email">
				<span v-show="errors.has('scope.field')">{{ errors.first('scope.field') }}</span>
				<span v-show="fields.$scope && fields.$scope.email && fields.$scope.email.dirty">I'm Dirty</span>
			</div>

		警告:
			注意在实际标志检查之前,先进行额外的检查,这是因为在 mount() 生命周期事件之前,标志实际上不可用,所以为了避免 created() 生命周期错误,我们需要添加这些检查。

		PS:
			所谓的额外检查就是上面的:
				先检查 fields.email,再检查 fields.email.dirty,因为 fields.email 在 created() 之前还不可用
				fields.$scope && fields.$scope.email && fields.$scope.email.dirty 同理

	mapFields 帮助程序
		如果我们引用多个标志,这些检查会变得非常繁琐,因此使用 mapFields 帮助程序可能非常有用,它类似于 Vuex 的 mapGetters 和 mapActions,因为它将一个字段对象映射为一个计算属性。

			import { mapFields } from 'vee-validate'

			export default {
			  	// ...
			  	computed: mapFields(['name', 'email', 'scope.phone']),
				// ...
			}

		我们也可以提供一个对象来重命名映射的属性:

			import { mapFields } from 'vee-validate'

			export default {
			  	computed: mapFields({
				    fullName: 'name',
				    phone: 'scope.phone'
			  	}),
			}

		提示:
			注意,数组中命名的作用域字段被映射为一个非嵌套名称

		我们可以使用对象的 '扩展运算符'(...),将要映射的字段添加到现有的计算组件:

			import { mapFields } from 'vee-validate'

			export default {
			  	computed: {
				  	...mapFields(['name', 'email', 'scope.phone']),
				  	myProp(){

				  	}
			  	},
			}

		此外,如果我们想手动设置标志,可以使用 Validator.flag(fieldName,flagsObj) 方法:

			// 将该字段标志为 valid 和 dirty
			this.$validator.flag('field', {
			  	valid: false,
			  	dirty: true
			});

			// 为作用域字段设置标志
			this.$validator.flag('scoped.field', {
			  	touched: false,
			  	dirty: false
			});

		对于自定义组件,为了使标志完全可靠地工作,我们需要 emit 以下事件:

		input 事件(我们可能已经 emit 过了),将设置 dirty 和 pristine 标志。

			this.$emit('input', value);

			// focus 事件将设置 touched 和 untouched 标志
			this.$emit('focus');

Validation Events
	vee-validate 在我们的 input 输入框上监听一组特定事件,这些事件一旦触发,就会触发该字段的验证。默认情况下,vee-validate 监听 input 事件。

	如果 input 事件验证对用户来说过于激进,我们可以选择其他触发器(像:change)来触发验证。我们可以配置 vee-validate 的默认事件监听,甚至为特定字段指定特定事件。

	改变默认事件

		Vue.use(VeeValidate, {
			events: 'change'
		});

		如果我们希望监听多个事件,包括自定义事件。只需要使用一个用 '|' 分隔的事件名列表:

			Vue.use(VeeValidate, {
				events: 'change|custom'
			});

	改变每个字段的事件
		我们可以使用 '与 events 配置具有相同值的'  data-vv-validate-on 属性,来指定字段进行验证的事件:

			<input name="field" v-validate="'required'" data-vv-validate-on="change|custom">

	禁用事件验证
		我们可能希望禁用由事件触发的所有验证,例如,我们只想在用户点击提交按钮后进行验证,我们可以通过为 events 配置,指定一个空字符串来实现,该字符串将禁用所有字段的所有监听器。

			Vue.use('VeeValidate', {
				events: ''
			});

		或者在 'v-validate' 指令上使用 '.disable' 修饰符,禁用指定字段的事件验证

			<input name="field" v-validate.disable="'required'">

		稍后在我们的代码中,一旦用户提交表单,可以调用 this.$validator.validate() 来触发验证:

			export default {
			  	// ...
			  	methods: {
			    	onSubmit () {
			      		this.$validator.validate().then(result => {
				        	if (!result) {
				          		// 如果无效,做一些提示
				        	}
			      		});
			   		}
			  	}
			  	// ...
			}

Validating Dynamically Displayed Inputs - 验证动态显示输入
	一个常见的情况是使用 v-if 基于某种条件,显示一个或两个字段,例如,如果用户来自美国,显示一个 'state' 字段,如果不是则隐藏它。或使用 v-for 从 JSON 数据生成一个 inputs 输入框列表。这完全被 vee-validate 所支持,但有一点需要注意。

	下面是一个来自 Vue 文档的 '关于 input 输入框被重用的' 引用:
		Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。

	官方示例使用以下模板:
		<template v-if="loginType === 'username'">
		  	<label>Username</label>
		  	<input placeholder="Enter your username">
		</template>
		<template v-else>
		  	<label>Email</label>
		  	<input placeholder="Enter your email address">
		</template>

	处理 v-if
		在尝试验证这些 inputs 输入框时,似乎 vee-validate 认为它们是相同的,取决于最初渲染的 input。这是由于 Vue 实际上重新使用了 input,而且对于 vee-validate,由于 directive API 中的某些限制(v-validate 是一个组件),vee-validate 不知道 input 已被切换。因此我们需要通过在 input 上使用 key 属性来帮助它,key 属性会强制 Vue 独立地渲染这些元素。
			<template v-if="loginType === 'username'">
			  	<label>Username</label>
			  	<input placeholder="Enter your username" key="username-input">
			</template>
			<template v-else>
			  	<label>Email</label>
			  	<input placeholder="Enter your email address" key="email-input">
			</template>

		现在,验证这些字段将按预期运行,只需要记住不同字段的 key 属性应该是唯一的。

	处理 v-for
		在 v-for 中使用 index(索引) 作为键是不够的,因为添加/删除条目会使某些字段占用其他字段的键。因此,在用户交互添加/删除字段的情况下,以下内容不起作用:
		  	<div v-for="(input, idx) in inputs" :key="idx">
		    	<label>Username</label>
		    	<input placeholder="Enter your username" key="username-input">
		  	</div>

		为了解决此问题,尝试为循环中的每个键生成唯一 ID:

		  	<div v-for="input in inputs" :key="input.id">
		    	<label>Username</label>
		    	<input placeholder="Enter your username" key="username-input">
		  	</div>

		下面是一个基本的 ID 生成器:

			let id = 0;

			export default {
			  	data: () => ({
			    	inputs: []
			  	}),
			  	methods: {
			    	addInput () {
			      		this.inputs.push({
			        		id: id,
			        		value: null
			      		});
				      	// 下一个创建的 input 输入框的 ID +1
				      	id++;
			    	}
			  	}
			};

		这样可以确保每个创建的字段完全分离,Vue 不会尝试重用它们。

		提示:
			我们实际上可能希望 Vue 重用这些 inputs 输入框,确保我们在自己的项目环境中,正确处理 key 属性值。

UI Integrations
	这些库/帮助程序,使得 vee-validate 和我们喜欢的 UI 库/工具包,结合使用轻而易举。

	元素
		vee-element for Element
		vee-element - https://github.com/davestewart/vee-element
		Element - https://github.com/ElemeFE/element

Components

	Validation Components
		提示:
			该功能 v2.1.0 以后可用

		VeeValidate 附带了更高级用例的组件。这些组件提供了一种不同的验证方法,使用 'scoped slots'(范围插槽) 功能来传递验证状态和结果。

		当前有2个组件:
			ValidationProvider
			ValidationObserver

	Validation Provider
		ValidationProvider 组件是一个常规组件,它包装了我们的 inputs 输入框,并使用 scoped slogs(作用域插槽) 提供验证状态。

		使用 ValidationProvider 为每个字段验证状态,提供了隔离的作用域,并且不会在其插槽外,注入/改变任何内容。我们可以导入它并在需要时随时使用。使用验证上下文,允许我们应用类、标志以及传递状态给我们的模板。

		下面是一个简单的示例:

			<template>
				<div>
					<ValidationProvider rules="required">
						<div slot-scope="{ errors }">
							<input v-model="value" type="text">
							<span id="error">{{ errors[0] }}</span>
						</div>
					</ValidationProvider>
				</div>
			</template>

			<script>
				import { ValidationProvider } from 'vee-validate';

				export default {
					components: {
						ValidationProvider
					}
				};
			</script>

		它也适用于自定义组件,并解决了由于指令限制而难以正常实现的自我验证组件的认证问题。

		提示:
			要验证的字段必须具有 v-model 指令,以便该组件可以正确识别被验证的元素/组件。

		作用域插槽数据
			传递给作用域插槽的对象称为 validation context(验证上下文),它具有以下属性:

				名称 - 类型 - 描述
				errors - string[] - 错误消息列表
				valid - boolean - 当前认证状态
				flags - {[x: string]: boolean} - 映射对象状态的状态
				aria - {[x: string]: string} - 映射 aria 属性的对象。
				classes - {[x: string]: boolean} - 映射基于验证状态配置的类的对象。

			由于插槽作用域可以利用 ES6 的析构功能,(我们可以选择性地使用任何这些属性,并把我们觉得合适的属性传递给我们的插槽模板) | (我们可以选择加入任何这些属性,并根据需要传递给我们的插槽模板。)。上面的例子只需要 errors 数组。

		示例
			之前的简单示例验证简单的 HTML inputs 输入框,让我们提升一个档次,验证流行的第三方组件,例如:Vuetify's TextInput(https://vuetifyjs.com/en/components/text-fields)。

			基本示例:
				这会将错误消息传递给 Vuetify's 的文本字段组件。

					<ValidationProvider rules="required">
					  <VTextField slot-scope="{ errors }" v-model="value" :error-messages="errors" />
					</ValidationProvider>

				提示:
					ValidationProvider 是一个无渲染组件,意味着它不会渲染任何自己的内容。它只渲染其插槽,因此我们的插槽中只能有一个根元素,如果我们使用 template 标签,则可能导致渲染错误。

			手动验证
				在任何 providers 上触发验证都很简单,但它是选择性的。意味着我们需要显式调用 provider 实例上的验证。使用 refs 和公共方法 validate 以及 applyResult 使得这个过程变得轻而易举。

					<template>
						<div>
							<ValidationProvider rules="required" ref="myinput">
								<VTextField slot-scope="{ errors }" v-model="value" :error-messages="errors" />
							</ValidationProvider>

							<v-btn @click="validateField('myinput')" >Submit</v-btn>
						</div>
					</template>

					<script>
					export default {
						// ...
						methods: {
							validateField (field) {
								const provider = this.$refs[field];

								// 验证字段,但是不改变字段的状态
								provider.validate().then(
									// 改变状态
									provider.applyResult
								);
							}
						},
						// ..
					};
					</script>

				提示:
					使用同样的方式,我们可以使用公共方法 reset() 来重置 provider 的验证状态。

			input 输入框组
				类似单选框或复选框(有时),某些 inputs 输入框的行为类似单个 input 输入框。我们可以将整个 inputs 输入框组,包装在单个 Validation provider 组件中,并给它们相同的 v-model。我们可以在 Validation provider 组件中组合任意多的 inputs 输入框。

					<ValidationProvider rules="required">
					  	<div slot-scope="{ errors }">
						    <input type="radio" v-model="drink" value="">
						    <input type="radio" v-model="drink" value="coffe">
						    <input type="radio" v-model="drink" value="coke">
					  	</div>
					</ValidationProvider>

			需要确认的/基于目标的验证
				使用指令时,confirmed 规则把 '匹配到 ref 的其他字段' 作为目标对象。而使用 ValidationProvider 稍有不同,它会查找 '匹配到 vid 属性的其他 provider 组件' 作为目标对象,vid 属性可以是数字或字符串。

					<ValidationProvider rules="required|confirmed:confirm">
					  	<VTextField slot-scope="{ errors }" v-model="password" type="password" :error-messages="errors" />
					</ValidationProvider>

					<ValidationProvider vid="confirm" rules="required">
					  	<VTextField slot-scope="{ errors }" v-model="passwordConfirm" type="password" :error-messages="errors" />
					</ValidationProvider>

		重构 Validation Providers
			ValidationProvider 虽然有它的优点,但它比使用指令更冗长,并且在创建大型表单时非常烦人,有几种方法可以解决这个问题。

			创建高阶组件
				React中的一个常见模式是,使用高阶组件来生成行为略有不同的新组件。除了使用 props/events 来传递状态,它类似于为我们的组件创建包装器或 mixin。

				withValidation 方法接收一个组件,并创建一个启用了验证行为的新组件。让我们使用该方法,来创建一个 VTextFieldWithValidation 组件:
					import { withValidation } from 'vee-validate';
					import { VTextField } from 'vuetify/lib';

					const VTextFieldWithValidation = withValidation(VTextField, ({ errors }) => ({
					  	'error-messages': errors
					}));

					export default {
					  	components: {
					    	VTextFieldWithValidation
					  	}
					};

				提示:
					注意,第二个参数是一个函数,它将验证上下文转换为 props 对象,传递给包装的组件。在本例中,我们希望将 errors 数组作为 error-messages 属性传递给 VTextField 组件。

				以这种方法,最后一个例子变成如下内容:

					<VTextFieldWithValidation rules="required|confirmed:confirm" v-model="password" />

					<VTextFieldWithValidation vid="confirm" rules="required" v-model="password" />

				警告:
					这种方法有一些缺点,例如,如果被包装的组件接受与 ValidationProvider 组件同名的 props - 虽然它会接收它们 - 它可能是不同的类型,可能会导致严重的问题。HOCs 的问题是,我们需要了解底层的组件实现,对于第三方组件,这可能是个问题。

			手动包装组件
				相反,我们可以使用 ValidationProvider 将字段组件包装在新组件中。这更加简单灵活,而且不会有任何 HOC 问题。

				考虑这个新的 VTextFieldWithValidation 组件

					<template>
					  	<ValidationProvider :rules="rules">
					    	<VTextField slot-scope="{ errors }" v-model="innerValue" :error-messages="errors" />
					  	</ValidationProvider>
					</template>

					<script>
						import { ValidationProvider } from 'vee-validate';

						export default {
						 	props: {
							    rules: [String],
							    value: null
						  	},
						  	components: {
					    		ValidationProvider
						  	},
						  	data: () => ({
						  		innerValue: null
						  	}),
						  	watch: {
						  		innerValue (val) {
							  	    this.$emit('input', val);
						  		}
						  	}
						};
					</script>

				理想情况下,我们将所需的 props 传递给 ValidationProvider 或被验证的 VTextField,这种方法可以解决冗长的问题,同时保留简单的作用域插槽 API。它还允许我们在没有冲突问题(与 HOC 不同)的情况下分发 props。

				使用哪种一种方法取决于我们

			参考
				以下是 ValidationProvider 的公共 API 参考

				Props
					以下所有的 props 都是可选的

					Prop - 类型 - 默认值 - 描述
					rules - string - undefined - 验证规则
					vid - string - 自增数字 - 用于 '基于目标/跨字段' 规则的标识符
					immediate - boolean - false - 是否在渲染(初始)之后立即验证该字段
					events - string[] - ['input'] - 将触发验证的事件
					name - string - undefined - 一个字符串,用于替换错误消息和自定义错误消息中的 {field}
					bails - boolean - true - 如果设置为 true,验证会在第一次验证失败后停止
					debounce(消抖) - number - 0 - 验证消抖,指定的毫秒数

			Methods
				这些是公共使用的、仅有的方法,其他可能存在于 ValidationProvider 的方法,是严格内部的。

					Method - 参数 - 返回值 - 描述
					validate - void - Promise<ValidationResult> - 根据定义的规则对当前值进行验证。不会改变验证状态
					applyResult - ValidationResult - void - 获取验证结果对象,并将其应用于当前状态
					reset - void - void - 重置验证状态

				Events
					validation provider 不会 emit 任何事件。

	Validation Observer
		使用 providers 进行验证非常方便,但它引入了一些自己的使用问题,例如,我们如何知道当前状态。比方说,我们想要禁用一个按钮,只要表单无效,就一直禁用该按钮,我们会怎么做?

		ValidationObserver 是一个方便的组件,它也使用了 '作用域插槽' 功能来沟通 inputs 整体的当前状态。

		下面是一个小的示例,同样是由 Provider 的 wrap 方法包装的 Vuetify 组件:

			<ValidationObserver>
			  	<form slot-scope="{ invalid }" @submit.prevent="submit">
			    	<InputWithValidation rules="required" v-model="first" :error-messages="errors" />

			    	<InputWithValidation rules="required" v-model="second" :error-messages="errors" />

			    	<v-btn :disabled="invalid">Submit</v-btn>
			 	</form>
			</ValidationObserver>

		提示:
			ValidationObserver 是一个无渲染组件,意味着它不会渲染任何自己的内容。它只渲染其插槽,因此我们的插槽中只能有一个根元素,如果我们使用 template 标签,则可能导致渲染错误。

		作用域插槽数据
			给作用域插槽传递一个包含了 flags 对象的对象,该对象表示在 observer 下注册的所有 providers 的合并状态。它包含以下属性:

				名称 - 类型 - 描述
				dirty - boolean - 如果至少有一个字段是 dirty,则为 true
				pristine - boolean - 如果所有字段是 pristine(非 dirty),则为 true
				valid - boolean - 如果所有字段是有效的,则为 true
				invalid - boolean - 如果至少有一个字段是无效的,则为 true
				pending - boolean - 如果至少有一个字段正在验证过程中,则为 true
				touched - boolean - 如果至少有一个字段被 touched(blurred - 获取过焦点),则为 true
				untouched - boolean - 如果所有字段都没有被 touched(blurred - 获取过焦点),则为 true
				errors - { [x:string]: string[] } - 一个包含了对每个字段错误的引用的对象,每个字段的 key 是它的 'vid' prop。

		示例:
			提交前验证
				提交前验证比旧的方式更容易,使用公共方法和一个简单的 ref,我们可以在提交表单之前验证所有的 providers。

					<template>
					  	<ValidationObserver ref="observer">
					    	<form slot-scope="{ invalid }" @submit.prevent="submit()">
					      		<InputWithValidation rules="required" v-model="first" :error-messages="errors" />

					      		<InputWithValidation rules="required" v-model="second" :error-messages="errors" />

					      		<v-btn :disabled="invalid">Submit</v-btn>
					    	</form>
					  	</ValidationObserver>
					</template>

					<script>
						export default {
						  	methods: {
						    	async submit () {
						      		const isValid = await this.$refs.observer.validate();
						      		if (!isValid) {
						        		// ABORT!!
						      		}

						      		// ? ship it
						    	}
						  	}
						};
					</script>

				提示:
					使用同样的方式,我们可以使用公共方法 reset() 来重置 provider 的验证状态。

			作用域和组
				验证组件 API 没有实现作用域,而且将来也不打算实现,我们可以使用 ValidationObserver,通过使用多个 observers 和 refs,将我们的字段进行分组,而没有作用域 API 的复杂性。

					<template>
					  	<div>
					    	<ValidationObserver ref="obs1">
					      		<div slot-scope="{ invalid }">
					       			<!-- Fields -->
					      		</div>
					    	</ValidationObserver>

					    	<ValidationObserver ref="obs2">
					      		<div slot-scope="{ invalid }">
					        		<!-- Fields -->
					      		</div>
					    	</ValidationObserver>
					  	</div>
					</template>

					<script>
						// 在一个方法的某处 ...
						// 验证第一个 observer.
						this.$refs.$obs1.validate();

						// 验证第二个 observer.
						this.$refs.$obs2.validate();
					</script>

				简单干净。

		参考
			下面是 ValidationObserver 公共 API 的参考

			Props
				validation observer 不接收任何的 props。

			Methods
				这些是公共使用的、仅有的方法,其他可能存在于 ValidationProvider 的方法,是严格内部的。

					Method - 参数 - 返回值 - 描述
					validate - void - Promise<boolean> - 验证所有子 providers,并改变它们的状态
					reset - void - void - 重置所有子 providers 的验证状态

			Events
				验证 observer 不会 emit 任何事件。

 

Logo

前往低代码交流专区

更多推荐