项目演化系列--验证体系,基于angular的前端验证

前言

  之前分享的《web项目演化--验证体系》中提到基于angular的验证,但是为了以防读者迷惑,能够好的理解验证体系,所以没有详细介绍。

  今天特地补写一篇关于构建angular的验证。

  前端验证组件虽然对于美工的要求会更多,但是真实的应用环境下,其实对业务的要求会更高,需要组件有统一的规范,除非设计通用组件,否则应该更关注业务来进行设计。

  有了统一的验证组件,复合组件可以重用单一的文本框、输入框、复选框等组件,开发人员可以节省不少时间,并且在后期需要对网站风格统一调整的时候,只需要改变相应的组件即可完成变更。

实现

  首先需要定义一个验证的规则,需要包含验证的方式、验证参数,比如:['stringLength', { min: 1, max: 6 }],这是我当前使用的验证体系的规则,由于anuglar的便利性,我们可以直接使用一个属性来存储该值。

  有了规则,就需要一个验证的类来验证以上定义的规则,大致代码如下:

demo.factory('$validator', [
        function () {
            return {
                stringLength: function (opts, value, callback) {
                    if (typeof value !== 'string' && value != null)
                        value = value.toString();

                    var min = opts.min || 0;
                    var max = opts.max || 0;
                    callback(value ? (max == 0 ? value.length >= min : value.length >= min && value.length <= max) : min === 0);
                }
            };
        }
]);

  验证方法中之所以使用异步回掉方式,是为ajax验证方式预留的。

  有了验证类,只要在组件中进行相应的引用,然后获取存有验证规则的属性,并用验证类进行验证即可,代码如下:

demo.directive('ucInput', [
        '$validator',
        function ($validator) {
            return {
                replace: true,
                scope: {
                    data: '='
                },
                template: '<input ng-background-color\': isValid && \'white\' || \'red\' }" type="text" ng-model="data" />',
                link: function (scope, el, attrs) {
                    scope.isValid = true;

                    var validType = scope.$parent.$eval(attrs.validType);
                    el.bind('blur', function () {
                        var fn = function () {
                            scope.isValid = $validator[validType[0]](validType[1], scope.data);
                        };
                        var phase = scope.$root.$$phase;
                        if (phase == '$apply' || phase == '$digest') {
                            if (fn && (typeof (fn) === 'function'))
                                fn();
                        }
                        else {
                            scope.$apply(fn);
                        }
                    });
                }
            };
        }
]);

  组件的效果比较丑,别在意细节(^_^),当用户焦点离开组件的时候,组件就会进行验证,因此该组件就需要用户的主动操作才能有效果了。

  然而如果用户不操作组件,直接点击提交按钮那该怎么办呢?

  于是乎就需要一个容器指令,来包裹所有的验证组件,并在触发提交按钮的时候,也可以让包含在内的所有验证组件自己触发验证。

  我们不可能在每一个验证组件中都写一堆相似的验证代码来实现验证,而且业务中,如果组件移除或者隐藏都应该将组件排除到验证列表外,因此就需要一个额外的类来承担这个责任,代码如下:

demo.directive('ucForm', [
        function () {
            return {
                require: 'ucForm',
                controller: [
                                function () {
                                    this.controls = [];
                                }
                ],
                link: function (scope, _, attrs, formCtrl) {
                    scope.onSubmit = function (callback) {
                        var ep = new EventProxy();
                        ep.after('valid', formCtrl.controls.length, function (res) {
                            var b = true;
                            for (var i = 0; i < res.length; i++) {
                                b = res[i];
                                if (!b)
                                    break;
                            }
                            if (b)
                                callback();
                        });

                        for (var i = 0; i < formCtrl.controls.length; i++) {
                            formCtrl.controls[i].valid(function (b) {
                                ep.emit('valid', b);
                            });
                        }
                    };
                }
            };
        }
]);

demo.factory('$valid-ui', [
        '$validator',
        function ($validator) {
                function Control(validType, property) {
                    this.validType = validType;
                    this.disable = false;
                    this.isValid = true;
                    this.property = property;
                    this.value = '';
                }

                Control.prototype.valid = function (callback) {
                    this.isValid = true;
                    if (this.disable && callback)
                        return callback(true);

                    var self = this;
                    $validator[this.validType[0]](this.validType[1], self.value, function (b) {
                        if (callback)
                            callback(b);

                        self.isValid = b;
                    });
                };

                return {
                    create: function (validType, property) {
                        return new Control(validType, property);
                    }
                };
        }
]);

  有了以上的支持,接下来只需要修改一个ucInput,并且将ucForm.onSubmit绑定到提交按钮上便可以实现一键验证的效果了,具体的实现代码这里就不提供了。

结束语

  那么基于angular的验证到这里就基本实现了,其他的扩展,例如:点击提交按钮的时候可以跳转到第一个错误组件的地方,只需要组件中通过获取组件坐标并滚动到相应的位置即可实现。

  至于美观方面,就得看美工了,文章就到这里了,如果有错误或者问题的话,请大家留言,谢谢。