I am currently developing a versatile service intended for use across various independent websites. However, there are certain instances where the service needs to execute different code based on the specific website it is implemented in. My goal is to maintain the separate per-website code from the core service.
Below is an example of the design I am aiming for (although it is not functioning as expected):
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>
I have explored several potential solutions to address this issue, but none seem ideal.
The most straightforward approach would involve replacing the baseService
service with a provider and allowing it to be configured within each module. This is commonly used for configuring services in other modules. However, I encountered limitations when attempting to access the website1Service
and website2Service
within the provider functions, as services cannot be accessed in provider functions during the configuration phase according to the documentation:
During application bootstrap, before Angular goes off creating all services, it configures and instantiates all providers. We call this the configuration phase of the application life-cycle. During this phase, services aren't accessible because they haven't been created yet.
Another workaround involves using angular.injector
to locate the appropriate service. However, the documentation for angular.injector
suggests that this method is primarily reserved for interacting with third-party libraries rather than internal implementations.
Lastly, I could introduce a dependency on a non-existent service (e.g., "baseServiceActions"
) in baseModule
, requiring a service with that name to be implemented in website1
and website2
. The dependency injection should then manage the connections when utilizing baseService
. However, this unconventional approach may lead to confusing error messages if the baseServiceActions
module is missing in a new website implementing the baseModule
.
Is there a more effective strategy to achieve this? If so, is it possible to modify the provided sample code to ensure that all tests pass without altering the testing logic?