如何在Angular中基于一个http调用进行多次验证

时间:2016-11-01 14:35:21

标签: javascript angularjs validation asynchronous custom-validators

我在表单上有一个包含大量验证的字段。

首先,我将其结构化为多个指令,每个指令都有自己的错误消息。

然而,验证使用后端异步调用,因此突然对于一个字段我正在为同一个数据服务进行5次http调用。我想弄清楚如何更有效地写这个。

我想知道是否有可能在$async之后有一个调用dataservice的$validators验证器和第一个异步函数内的多个常规.then。我试验过这个,但它似乎根本没有达到嵌套$validators

我还试图在服务中进行一次调用,但是当字段上的modelValue发生更改时,我不知道如何更新它,并因此将信息传递给相应的验证指令。我可以将此作为服务中的异步验证,并将响应附加到要查找的指令的范围吗?

TLDR;

如何进行一次http调用并根据返回的数据执行多次验证检查,每次都有自己的错误?

例如

我有四个看起来像这样的指令:

angular.module('validationForField').directive('po', ['$q', '$sce', '$timeout', 'myService', function ($q, $sce, $timeout, myService) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl, ngModel) {
            ctrl.$asyncValidators.validateField = function (modelValue) {
                var def = $q.defer();
                myService.httpcall(modelValue)
                    .then(function (response, modelValue) {
                        if (response.data.status === "Error") {  
                            return def.reject();
                        }
                     def.resolve();

                    }).catch(function(){
                    def.reject();
                });
                return def.promise;
            }
        }
    }
}]);

每个人都有不同的数据分析,以返回不同的错误消息。每个人都调用myService.httpcall,这最终是多余的,因为他们都获得了相同的数据。

我正在尝试

angular.module('validationForField').directive('po', ['$q', '$sce', '$timeout', 'myService', function ($q, $sce, $timeout, myService) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl, ngModel) {
            ctrl.$asyncValidators.validateField = function (modelValue) {
                var def = $q.defer();
                myService.httpcall(modelValue)
                    .then(function (response, modelValue) {
                        if (response.data.status === "Error") {  
                            return def.reject();
                        }
                        ctrl.$validators.checkStatus = function (response) {
                             if (response.data.data.status === "10"){
                             return false
                         }
                        ctrl.$validators.checkPermissions = function (response) {
                             return response.data.data.permission){

                         }

                     def.resolve();

                    }).catch(function(){
                    def.reject();
                });
                return def.promise;
            }
        }
    }
}]);

通过这种方式,主要的异步验证器是关于http调用是否成功,以及内部$ validators在返回时使用该数据

1 个答案:

答案 0 :(得分:1)

我假设后端服务接受一个值(要验证的字段的值)并返回所有验证的单个响应,例如:

// true would mean valid, string would mean invalid with the given error:
{
    businessRuleOne: true,
    businessRuleTwo: "The format is incorrect",
    ...
}

我相信解决方案是在缓存承诺的服务中执行HTTP调用;异步验证器调用服务并检索它们返回的相同promise。一些带内联解释的示例代码:

// the service:
app.service('myService', function($http, $q) {
    // cache the requests, keyed by the model value
    var requestMap = {};

    this.httpcall = function(modelValue) {
        // if cached, return that (and do not make extra call)
        if( requestMap[modelValue] ) {
            return requestMap[modelValue];
        }
        // if not cahced, make the call...
        var promise = $http.get('....');
        // ...cache it...
        requestMap[modelValue] = promise;
        // ...and remember to remove it from cache when done
        promise.finally(function() {
            delete requestMap[modelValue];
        });
        return promise;
    };
});

现在可以完全按照发布的方式实现异步验证器。调用myService.httpcall(modelValue)只会在第一次调用时调用远程服务,其余的将重用缓存的promise。

还有两点:(1)这种技术称为memoization。它由许多库实现,例如lodash,您可以使用它们来保持myservice.httpcall()清洁。 (2)您不需要异步验证器的额外承诺,例如:

angular.module('validationForField').directive('po', ['$q', '$sce', '$timeout', 'myService', function ($q, $sce, $timeout, myService) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl, ngModel) {
            ctrl.$asyncValidators.validateField = function (modelValue) {
                return myService.httpcall(modelValue)
                    .then(function (response) {
                        if (response.data.status === "Error") {  
                            return $q.reject();
                        }
                        return response;
                    });
            }
        }
    }
}]);