如何根据 Angular 中的一次 http 调用进行多次验证

How to do multiple validations based on one http call in Angular

我在表单上有一个字段需要大量验证。

起初,我将它构造成多个指令,每个指令都有自己的错误消息。

但是,验证使用后端异步调用,所以突然间我为一个字段对同一个数据服务进行了 5 个 http 调用。我想弄清楚如何更有效地编写它。

我想知道是否可以在 .then 之后的第一个异步函数中有一个 $async 调用数据服务的验证器和多个常规 $validators。我对此进行了试验,但它似乎根本没有到达嵌套的 $validators

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

TLDR;

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

例如

我有大约四个指令都是这样的:

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;
            }
        }
    }
}]);

每个人对数据的分析不同,导致return不同的错误信息。每个人都调用 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 调用是否成功的主要异步验证器,以及在 returns

时使用该数据的内部 $validators

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

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

我认为解决方案是在缓存承诺的服务中执行 HTTP 调用;异步验证器调用该服务并检索相同的承诺,他们 return。一些带有内联解释的示例代码:

// 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;
    };
});

现在可以像您一样实现异步验证器 post。调用 myService.httpcall(modelValue) 只会在第一次调用时调用远程服务,其余的将重用缓存的承诺。


还有两点:(1) 这种技术称为记忆化。它由许多库实现,例如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;
                    });
            }
        }
    }
}]);