您如何设计一个用户在依赖它时必须将配置数据传递给的模块?

How do you design a module that a user has to pass config data to when they depend on it?

我有一个 angular 模块,它基本上是一个客户端 Web 服务层(正在进行中)。我有一个模块声明和一个名为 "baseRequest" 的工厂。 baseRequest 依赖于模块中的其他工厂。 BaseRequest 需要知道它将调用的 WebApi2 控制器的基础Url。

该脚本托管在站点 A:但将在站点 B、C、D、E 等上使用。因此,如果没有某些内容,它无法可靠地知道它是自己的 url配置数据。

基于此,我希望使用 api 的用户在添加脚本标签时必须对其进行配置。我设想他们必须在我的模块上调用配置,但我不确定如何在这个模块上实现它,并且被有关该主题的文档弄糊涂了:

(function () {
angular.module('xyzApi', []).run(['$http', function ($http) {
    //Configure defaults on $http here if need be
}]).factory('xyzApiEvents', ['$rootScope', function ($rootScope) {
    var evtNotFoundErr = 'xyzAPI Does not have an event called: ';
    var events = {
        "loggingIn": "xyzApi.loggedIn",
        "loggedOut": "xyzApi.loggingIn",
        "loggingIn": "xyzApi.loggingOut",
        "loggedOut": " xyzApi.loggedOut"
    }
    var factory = {
        raiseEvent: function (eventName, eventValue) {
            if (!events.hasOwnProperty(eventName))
                throw evtNotFoundErr + eventName;
            $rootScope.$broadcast(events[eventName], eventValue);
        },
        on: function (eventName, callBack) {
            if (!isFunction(callBack))
                throw 'xyzApi: xyzApiEvents.on(eventName, callBack)... callBack must be a function!';
            if (!events.hasOwnProperty(eventName))
                throw evtNotFoundErr + eventName;
            $rootScope.$on(events[eventName], function (evt, args) {
                callBack(evt, args);
            });
        }
    };
    return factory;
}]);
function isFunction(functionToCheck) {
    var getType = {};
    return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}

})();

基本请求:

(function () {
    angular.module('xyzApi').factory('baseRequest', ['$q', '$http', '$log', 'xyzApiEvents', function ($q, $http, $log, xyzApiEvents) {
        var userOAuthToken = null;
        var httpConfig = { headers: {} };
        xyzApiEvents.on('loggingIn', function (event, oAuthToken) {
            userOAuthToken = oAuthToken;
            //set Authorization header for authorized web requests
            httpConfig.headers['Authorization'] = userOAuthToken;            
        });
        var baseUrl = '{{baseUrl}}'; //this is the url I need configured
        return {
            serviceUrl: function () {
                return baseUrl;
            },
            get: function (sQuery) {
                var deferred = $q.defer();
                var url = baseUrl + sQuery;
                $http.get(url, httpConfig).then(function(response) {
                    deferred.resolve(response);
                }, function (errorResponse) {
                    $log.error(errorReponse);
                    deferred.reject(errorResponse);
                }); //TODO Add Update Promise
                return deferred.promise
            },
            post: function (sQuery, postData) {
                var deferred = $q.defer();
                var url = baseUrl + sQuery;                
                $http({
                    method: 'POST',
                    url: url,
                    headers: httpConfig.headers,
                    data: postData
                }).then(function (response) {
                    deferred.resolve(response);
                }, function (errorResponse) {
                    $log.error(errorResponse);
                    deferred.reject(errorResponse);
                }); //TODO Add Update Promise
                return deferred.promise;
            }
        }
    }]);
})();

解决方案(感谢@Anzeo)

我有许多单独的服务,我将它们打包成一个服务作为 API 中的最后一个脚本:所以我将其设为提供者而不是将 baseRequest 设为提供者。此外,我必须将我的模块的名称更改为 xyzDataWebServices,因为我希望 xyzApi 成为 controller/directives 中使用的代码,并且提供者不能与其模块同名(这导致了问题).

(function () {
    angular.module('xyzDataWebServices').provider('xyzApi', function () {
        var baseUrl;
        this.setBaseUrl = function (value) {
            baseUrl = value;
        };
        this.$get = ['$log', 'baseRequest', 'xyzApiContext', 'xyzApiAuthentication', 'xyzApiLibrary', function xyzApiFactory($log, baseRequest, xyzApiContext, xyzApiAuthenication, xyzApiLibrary) {
            baseRequest.setServiceUrl(baseUrl);
            var factory = {
                context: xyzApiContext,
                authentication: xyzApiAuthenication,
                library: xyzApiLibrary
            }
            return factory;
        }];
    });
})();

然后依赖它并配置服务Url:

<script type="text/javascript">
    angular.module('app', ['ngAnimate', 'ngAria', 'ngMessages', 'ngTouch', 'ui.bootstrap', 'ui.router', 'xyzDataWebServices']).config(['$stateProvider', '$urlRouterProvider', 'xyzApiProvider', function ($stateProvider, $urlRouterProvider, xyzApiProvider) {
        xyzApiProvider.setBaseUrl('@Utility.DataWebServiceUrl');
        $urlRouterProvider.otherwise('/');
        $stateProvider.state('login', {
            url: '/',
            templateUrl: 'assets/templates/login.html'
        });
    }]).factory('loginModel', [function () {
        return {
            userName: null,
            password: null,
            stayLoggedIn: false
        };
    }]);
</script>

我应该注意这是 MVC 5、Web Api 2 和 Angular。所以上面是在 Razor 模板 "Cshtml" 中,@Utility 是在 Utility class 上调用该方法的 Razor 语法。因此,当视图转换时,它会将 Web 服务 url 存储在 web.config 文件中。

我还使用 Web 配置转换来根据我部署到的服务器更改 Url。

简而言之,此 url 配置现在由部署驱动配置,我永远不必更改它。

注意* 我将所有内容都包含在函数中的原因是因为我的 API 被拆分成物理文件:例如

前面的数字只是为了方便按字母顺序排列,并且很容易更改以更改捆绑顺序。

xyzApi 是捆绑顺序中的第一个,然后服务按带通配符的字母顺序捆绑,然后 xyzApiEnd 最后捆绑。

您可以使用 Angular 的 Provider 机制实现可配置库。

基本上您要做的是围绕 service/factory/whatever 编写一个包装器。您可以将此包装器作为提供者注入 config 语句(即 `$httpProvider)。

angular.module('xyzApi').provider('baseRequest', function () {
});

然后您可以公开方法来为稍后(在 运行 阶段)使用的 service/factory 设置变量。

在您的情况下,您需要添加一个方法以允许用户设置 baseUrl,例如:

angular.module('xyzApi').provider('baseRequest', function () {
    var baseUrl;
    return {
        setBaseUrl: function (providedBaseUrl) {
            baseUrl = providedBaseUrl;
        }
    }
});

最后,添加特殊的$get方法。 $injector 服务使用此方法来获取注入的实例 属性。

angular.module('xyzApi').provider('baseRequest', function () {
    var baseUrl;
    return {
        setBaseUrl: function (providedBaseUrl) {
            baseUrl = providedBaseUrl;
        },
        $get: function(/* here you can use other injected services */){
        }
    }
});

我总是将服务的实现包装在创建函数中。

将这些更改应用于您的代码会产生:

angular.module('xyzApi').provider('baseRequest', function () {
    var baseUrl;
    return {
        setBaseUrl: function (providedBaseUrl) {
            baseUrl = providedBaseUrl;
        },
        $get: function ($q, $http, $log, xyzApiEvents) {
            return createBaseRequestFactory($q, $http, $log, xyzApiEvents, baseUrl);
        }
    };

    function createBaseRequestFactory($q, $http, $log, xyzApiEvents, baseUrl){
        var userOAuthToken = null;
        var httpConfig = {headers: {}};
        xyzApiEvents.on('loggingIn', function (event, oAuthToken) {
            userOAuthToken = oAuthToken;
            //set Authorization header for authorized web requests
            httpConfig.headers['Authorization'] = userOAuthToken;
        });

        return {
            serviceUrl: function () {
                return baseUrl;
            },
            get: function (sQuery) {
                var deferred = $q.defer();
                var url = baseUrl + sQuery;
                $http.get(url, httpConfig).then(function (response) {
                    deferred.resolve(response);
                }, function (errorResponse) {
                    $log.error(errorReponse);
                    deferred.reject(errorResponse);
                }); //TODO Add Update Promise
                return deferred.promise
            },
            post: function (sQuery, postData) {
                var deferred = $q.defer();
                var url = baseUrl + sQuery;
                $http({
                    method: 'POST',
                    url: url,
                    headers: httpConfig.headers,


                    data: postData
                }).then(function (response) {
                    deferred.resolve(response);
                }, function (errorResponse) {
                    $log.error(errorResponse);
                    deferred.reject(errorResponse);
                }); //TODO Add Update Promise
                return deferred.promise;
            }
        }
    }
});