我正在编写一个将在多个独立网站中使用的服务。但是,在某些时候,它需要触发不同的代码,具体取决于它所使用的网站。我希望将每个网站代码与基本服务分开。
以下是一些示例代码,演示了我想要的设计(尽管它不起作用):
var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', function() {
this.func = function() {
return ["first",
/* TODO somehow get from appropriate
service in website module */
"FIXME",
"end"];
};
});
var website1 = angular.module('website1', ['baseModule']);
website1.service('website1Service', function() {
this.someCustomValue = function() {
// Note that while this is a constant value, in
// the real app it will be more complex,
// so replacing this service with a constant provider won't work.
return "someValue";
}
});
// TODO : somehow link website1Service.someCustomValue to baseService
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
this.anotherValue = function() { return "anotherValue"; }
});
// TODO : somehow link website2Service.anotherValue to baseService
// Testing code:
function makeTestController(expected) {
return ['$scope', 'baseService', function($scope, baseService) {
var result = baseService.func();
if (angular.equals(result, expected)) {
$scope.outcome = "Test Passed!";
} else {
$scope.outcome = 'Test failed...\n' +
"Expected: " + angular.toJson(expected) + '\n' +
"But got : " + angular.toJson(result);
}
}];
}
website1.controller('TestController1',
makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2',
makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
<div ng-controller='TestController1'>
<pre>{{outcome}}</pre>
</div>
</div>
<div id='website2'>
<h3>Website 2</h3>
<div ng-controller='TestController2'>
<pre>{{outcome}}</pre>
</div>
</div>
我想到了一些解决方案,但似乎都不是最佳解决方案。
最明显的方法是用提供程序替换baseService
服务,并允许在每个模块中配置它。这似乎是在其他模块中配置服务的标准方法。但是,我无法访问提供程序函数中的website1Service
和website2Service
,因为无法在提供程序函数中访问服务。这在文档中注明:
在应用程序引导期间,在Angular创建所有服务之前,它会配置并实例化所有提供程序。我们称之为应用程序生命周期的配置阶段。在此阶段,服务无法访问,因为尚未创建服务。
解决此问题的另一个解决方案是使用angular.injector
来查找正确的服务。但是,docs for angular.injector
暗示您实际上只需要与第三方库进行交互。所以似乎有更好的方法。
最后,我可以在"baseServiceActions"
中为不允许的服务(例如baseModule
)添加依赖项,并要求在website1
和website2
中实现具有该名称的服务。然后,在使用baseService
时,依赖注入应该将它们全部绑定在一起。但是,这是一种非常奇怪的工作方式,如果baseServiceActions
模块未在使用baseModule
模块的新网站中实现,则会导致错误消息。
有更好的方法吗?如果是这样,是否可以更改我发布的示例代码以使所有测试通过?理想情况下,不应更改任何测试代码。
答案 0 :(得分:0)
我最终找到了一个相当不错的解决方案。我创建了一个名为"<serviceName>Settings"
的服务,并为其添加了setup
函数。然后我在我想要使用它的模块中的模块运行块中调用该setup函数。最后,我在服务中调用了一个validate
方法,该方法使用设置来确保它已设置,并抛出一个好的错误消息,如果不是。这解决了我对其他解决方案遇到的所有问题。
这就是我的示例问题与此解决方案的关系:
var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', ['baseServiceSettings', function(baseServiceSettings) {
baseServiceSettings.validate();
this.func = function() {
return ["first",
baseServiceSettings.getValue(),
"end"];
};
}]);
baseModule.service('baseServiceSettings', function() {
this.setup = function(getter) {
this.getValue = getter;
};
this.validate = function() {
if (!this.getValue) {
throw "baseServiceSettings not setup! Run baseServiceSettings.setup in a module run block to fix";
}
};
});
var website1 = angular.module('website1', ['baseModule']);
website1.run(['baseServiceSettings', 'website1Service', function(baseServiceSettings, website1Service) {
baseServiceSettings.setup(website1Service.someCustomValue);
}]);
website1.service('website1Service', function() {
this.someCustomValue = function() {
// Note that while this is a constant value, in
// the real app it will be more complex,
// so replacing this service with a constant provider won't work.
return "someValue";
}
});
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
this.anotherValue = function() { return "anotherValue"; }
});
website2.run(['baseServiceSettings', 'website2Service', function(baseServiceSettings, website2Service) {
baseServiceSettings.setup(website2Service.anotherValue);
}]);
// Testing code:
function makeTestController(expected) {
return ['$scope', 'baseService', function($scope, baseService) {
var result = baseService.func();
if (angular.equals(result, expected)) {
$scope.outcome = "Test Passed!";
} else {
$scope.outcome = 'Test failed...\n' +
"Expected: " + angular.toJson(expected) + '\n' +
"But got : " + angular.toJson(result);
}
}];
}
website1.controller('TestController1',
makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2',
makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
<div ng-controller='TestController1'>
<pre>{{outcome}}</pre>
</div>
</div>
<div id='website2'>
<h3>Website 2</h3>
<div ng-controller='TestController2'>
<pre>{{outcome}}</pre>
</div>
</div>