这是一个封装的验证指令,比如在注册页面比较两次密码输入的是否一致,或者第一项是否大于第二项等等。
这个指令是看阿里的一个大神视频中学到的,里面除了修改一小部分,大部分都的逻辑结构都是和视频中一样的,看了视频收获很大,也发现了自己的一些问题
在工作中时常会碰到一些重复的页面,例如这次注册页面,有很多页面的逻辑其实都是相同的。为了提高效率,不妨把这些相同点都抽象出来,做成一个插件,以后用的时候,直接拿来用,效率会提高不少。
正好angular的自定义指令可以达到这个效果,把一些常用的逻辑都封装成一个个指令,以后在不同的项目上,直接引用即可。
下面来看一下代码:(并非原创)
/** * 获取字段值 * @param {Object} obj 当前指令所在作用域 * @param {String} key 支持xx.xxx.xx类似的形式 * @return {Object} 对应的值 */ function getFieldValue(obj, key) { } /** * 继承方法 * @param {Function} superClass 父类 * @param {Function} subClass 子类 */ function inherit(superClass, subClass) { var prototype = Object.create(superClass.prototype); prototype.constructor = subClass; subClass.prototype = prototype; } /** * 比较器策略类 * @param {string} operator 比较器 * @param {any} value 原始值 * @param {any} compareTo 比较值 */ function ValueCompareStrategy(operator, value, compareTo) { this.operator = operator; this.value = this.getValue(value); this.compareTo = this.getCompareTo(compareTo); } /** * 将不同的比较器注册到ValueCompareStrategy * @param {String} dataType 比较器类型 * @param {Function} fun 比较器构造函数 */ ValueCompareStrategy.regist = function (dataType, fun) { if (this[dataType]) { return false; } this[dataType] = fun; } /** * 根据数据类型,获取对应的比较器构造函数 * @param {String} dataType 数据类型 * @returns {Function} 数据类型对应的构造函数 */ ValueCompareStrategy.get = function (dataType) { return ValueCompareStrategy[dataType] || ValueCompareStrategy; } /** * 获取比较值,该值为比较操作左边的元素 * @param {any} value 比较数 * @returns {any} {*} 经过格式转换的值 */ ValueCompareStrategy.prototype.getValue = function (value) { return value } /** * 获取比较值,该值为比较操作右边的元素 * @param {any} compareTo 被比较值 * @returns {any} {*} 经过格式转换的比较值 */ ValueCompareStrategy.prototype.getCompareTo = function (compareTo) { return compareTo; } /** * 默认的比较方法 * @param {String} op 比较操作符 * @param {any} value 比较值 * @param {any} compareTo 呗比较值 * @returns {boolean} 经过格式转换的比较值 */ ValueCompareStrategy.prototype.compare = function (op, value, compareTo) { var value = this.value, compareTo = this.compareTo, result = true; switch (op) { case 'eq': result = (compareTo == value); break; case 'eqeqeq': result = (compareTo == value); break; case 'lt': result = (value < compareTo); break; case 'gt': result = (value > compareTo); break; case 'le': result = (value <= compareTo); break; case 'ge': result = (value >= compareTo); break; } return result; } /** * 验证比较值和被比较值是否合法,如果不揭发,则应该吧字段合法性的验证权交给下一个表单校验器 * @returns {boolean} 是否合法 */ ValueCompareStrategy.prototype.valueValidate = function () { return true; } /** * 初始化比较器 * 注意:做扩展时,需要首先定义对应数据格式的构造器,然后在该函数中添加对应的datatype即可 * @return {[type]} [description] */ function initValidateCompare() { /** * 支持比较数据类型常量 * @type {Object} */ var CONST = { DATATYPES: { number: NumberCompare, date: DateCompare } } var dataTypes = CONST.DATATYPES, constructor = ""; //将不同数据格式的比较器注册,并且继承默认的比较器策略类 for (var dataType in dataTypes) { if (dataTypes.hasOwnProperty(dataType)) { //? constructor = dataTypes[dataType]; ValueCompareStrategy.regist(dataType, constructor); inherit(ValueCompareStrategy, constructor); } } } initValidateCompare(); function NumberCompare() { ValueCompareStrategy.apply(this, Array.prototype.slice.call(arguments, 0)); } NumberCompare.prototype.getValue = function (value) { return value * 1; } NumberCompare.prototype.getCompareTo = function (compareTo) { return compareTo * 1; } NumberCompare.prototype.valuesValidate = function () { return this.value === this.value && this.compareTo === this.compareTo; } function DateCompare() { ValueCompareStrategy.apply(this.Array.prototype.slice.call(arguments, 0)); this.originValue = arguments[1]; this.originCompareToValue = arguments[2]; } DateCompare.prototype.getValue = function (value) { if (angular.isString(value)) { value = Date.parse(value); } else if (angular.isDate(value)) { value = value.getTime(); } else { //如果value的值不是时间字符串或者时间对象,则直接乘1做转换, //如果值是合法的数字,则不会对结果产生影响,如果值是NaN,则会在合法性中判断返回false,会把控制器交给其他验证器 value = value * 1; } return value; } DateCompare.prototype.getCompareTo = function (value) { if (angular.isString(value)) { value = Date.parse(value); } else if (angular.isDate(value)) { value = value.getTime(); } else { //如果value的值不是时间字符串或者时间对象,则直接乘1做转换, //如果值是合法的数字,则不会对结果产生影响,如果值是NaN,则会在合法性中判断返回false,会把控制器交给其他验证器 value = value * 1; } return value; } DateCompare.prototype.valueValidate = function () { return this.value === this.value && this.compareTo === this.compareTo; } angular.module('liveApp.directives').directive('compare', function () { return { require: 'ngModel', link: function (scope, el, attrs, ctrl) { var dataType = attrs.compareDataType || 'number'; var CompareHandler = ValueCompareStrategy.get(dataType); var compare = attrs.compare; // var obj = scope;//scope[attrs.compare] //var watch = attrs.watchCompareField; var watch=true; //暂时所有都监视 var op = attrs.compareOperation; if (watch !== false) { scope.$watch(compare, function () { var fromWatch = true; var value = ctrl.$modelValue; compareFiledValue(value, fromWatch); }) } ctrl.$parsers.unshift(compareFiledValue); //添加对当前指令的字段进行监控 function compareFiledValue(value, fromWatch) { /*如果是被监控字段的变化,则被监控字段变化所引起的当前字段的验证行为只做“通过”动作,不做“不通过的动作, 原因:如果不这样设定,则会出现其他字段编辑引发当前字段提示错误的情况,不符合自然思维。 相反,其他字段编辑从而使当前字段的值合法,则应该去掉当前字段的错误提示*/ //if (fromWatch) { // ctrl.$setValidity('compare', true); // return value; //} //ctrl.$setValidity('compare', true); //字段值为空时,不走比较逻辑,直接把合法性判断控制权交给下一个表单验证器。 if (value === undefined) { return value; } var compareTo = scope.$eval(compare); if (compareTo === undefined) { return value; } var compareHandler = new CompareHandler(op, value, compareTo); //两个比较字段的值不合法时 (比如格式不对,比如不是数字,比如不是object等等),也不走比较逻辑(因为肯定有其他的表单验证器 //if (!compareHandler.valuesValidate()) { // return true; //} var result = compareHandler.compare(op); ctrl.$setValidity('compare', result); return value; } } } })
基本用到了工厂模式,对每种类型的比较进行处理,页面中的使用也很方便,例如注册时密码和确认密码的比较:
<div class="form-group"> <div class="col-xs-12"> <input required password-valid name="password" type="password" class="form-control" ng-model="User.PassWord" placeholder="密码"/> </div> </div> <div class="form-group"> <div class="col-xs-12"> <input required watch-compare-field="{{true}}" name="passwordrepeat" ng-model="User.pwdRepeat" compare="User.PassWord" compare-data-type="string" compare-operation="eqeqeq" type="password" class="form-control" placeholder="确认密码"/> </div> </div>
因为项目是一个小例子,所以指令中的逻辑不会很全面,可以自行添加用到的逻辑。整理成自己的一个指令库。