Configuring shared services across multiple modules in AngularJS - angularjs

My app is following John Papa's styleguide for AngularJS applications:
The styleguide emphasizes using a strongly modular approach to the design of the app. My question is about multiple configurations and their effect on shared services.
Suppose I have a main module like this:
angular.module("app", ["app.feature1"])
.config(function() {
// do some configuration here
console.log("app configured");
});
And a feature module that configures a shared angular service, let's say the $http service:
angular.module("app.feature1", [])
.config(function($http) {
// configure the $http service
console.log("feature1 configured");
});
Is my understanding correct, that the configuration by "feature1" will carry over to the main module, since the $http service is a singleton and therefore shared across modules? Or do I have to configure the $http service in the main module instead, because each module has it's own $http service instance?
Edit: I can confirm that dependency configs are carried over and are executed first. See David's jsfiddle example.

As a matter of best practice, you should configure services as early as possible, which is typically your main module (the app root), and preferably only once to avoid overlapping changes.
Since $http is a singleton (as you mentioned), any changes via configuration will be propagated throughout the application every time you inject $http.
It's also worth mentioning that configuration to services is First In First Out, meaning that if you have two configuration changes, the last-accessed configuration will be the one that is persisted to the service since the previous configuration will be overwritten if they are changing identical components of the service.
In your case, yes, the change to $http in your module will be extended to your main application and any other modules using $http.
Finally, in light of the comments, child dependency configs are resolved before parent configs, as demonstrated by this simple fiddle:
http://jsfiddle.net/maqzo6fv/
HTML:
<div ng-app="app"></div>
JS:
angular.module("app", ["app.feature1"])
.config(function() {
alert('main config');
});
angular.module("app.feature1", [])
.config(function() {
alert('child config');
});
The child config will call alert before main on every load.

Related

Difference between dependency injection by passing argument and by using angular.injector

I had a use-case where I wanted to make the session timeout of the application configurable by making a call to REST api and get the value using $http. But as I found in this link how to inject dependency into module.config(configFn) in angular that services cannot be injected in config method, I have found this solution to make this work in the config:
var $http = angular.injector(['ng']).get('$http');
This is working fine, what is the difference between the two approaches and why this is working ? Also is there any limitation of using this approach.
angular.injector creates a new injector (application instance). It is misused most times and rarely ever needed in production.
It will have its own $http, $rootScope, $q, etc instances:
angular.injector(['ng']).get('$http') !== angular.injector(['ng']).get('$http');
The use of angular.injector in config block is an antipattern. It will result in untestable and potentially buggy code. $http request is asynchronous, application initialization will be completed first and likely result in race condition.
Most times when a need for $http in config appears, it should be performed in route resolver. If the response contains information that should be used to configure service providers and should be available in config block, it should be fetched prior to application bootstrap - in fact, there should be two applications, as shown here.

Selectively Mock Services when Testing Angular with Karma

While there have been many questions around mocking an individual Angular service in Karma, I am having an issue with making my mocks more ubiquitous throughout testing my application.
In my current setup, I have a module called serviceMocks that includes a factory with a mock of each service in the application.
Contrived example:
angular.module('serviceMocks',['ngMock'])
.factory('myServiceOne', function() {...})
.factory('myServiceTwo', function($httpBackend,$q) {...})
This works great when testing controllers and directives which may use one or more services as a dependency. I include my app's module and the serviceMocks module within my testfile, and each service is properly substituted.
beforeEach(module('myApp'));
beforeEach(module('serviceMocks'));
it('properly substitutes mocks throughout my tests', function() {...});
However, when testing a service itself I cannot include the serviceMocks module, as the service I am testing is substituted by its mock making the tests useless. However, I would still like all other services mocked as a service may depend on one or more services for its execution.
One way I thought of doing this was to make my service mocks globally available, perhaps by attaching an object to window that holds the mocks. I could then include the mocks individually when testing services like so:
beforeEach(module('myApp', function($provide) {
$provide.value('myServiceOne',window.mocks.myServiceOneMock);
$provide.value('myServiceTwo',window.mocks.myServiceTwoMock);
});
However this approach did not work, because some of the mocks use $q or other angular services to function properly, and these services are not properly injected when simply attaching the factory object to the window.
I am looking for a way to test services while having a single location to define mocks for all services. Possibilities I imagine but have been unable to succeed with:
A) Have the serviceMocks module's .run() block run before the
config stage for myApp's module. (In which case I could attach
each service to the window as the angular dependencies would be
properly injected, and inject each as shown above)
B) Be able to override the service that I'm testing with its actual implementation in the test files of each service
C) Otherwise be able to define and access these mocks globally, while still ensuring each mock has access to certain angular services such as $q.
The question contains a clue to the answer. If serviceMocks module causes design issues, using it is a mistake.
The proper pattern is to have one module per unit (mocked service in this case). ngMock is not needed, it is loaded automatically in Jasmine tests. The modules can be used one by one:
beforeEach(module('app', 'serviceOneMock', 'serviceTwoMock'));
Or joined together:
angular.module('serviceMocks', ['serviceOneMock', 'serviceTwoMock'])
There are not so many cases when serviceMocks module should exist at all. Just because a decision which services should be mocked and which should not is made for each describe block.
Most times mocked services are individual for current spec or depend on local variables. In this case the services are mocked in-place:
var promiseResult;
beforeEach(module('app'));
beforeEach(module({ foo: 'instead of $provide.value(...)' });
beforeEach(($provide) => {
$provide.factory('bar', ($q) => {
return $q.resolve(promiseResult);
}
});
...
Doing this in dedicated serviceOneMock, etc. modules may require mocked services to be refactored any moment it becomes obvious they are too generic and don't suit the case well.
If mocked service is used more than once in specs with slightly different behaviour and results in WET tests, it is better to make a helper function that will generate it for current spec rather than hard-coding it to serviceOneMock, etc. modules.

what are the parameters will pass throug by .config() and .run() in angularjs

I only see .config with passing $routeProvider, what else will passing through by .config and also want to know what are the parameters will pass through by .run in angularjs
These are dependency injected services.
So for config you should be asking for constant or provider. For run you should be asking for service, factory, value, constant.
You should notice i'm saying should be asking for, because we're dependency injecting the services, the function parameters are effectively asking angular DI for particular instances. If angular has one it will supply the function arg with that instance, similar to a fn.call({}, someDep)
So for example:
angular.module('myMod', [])
.provider('myFactory', myFactoryProvider)
.factory('anotherService', anotherServiceFactory)
.config(function (myFactoryProvider) {
})
.run(function (myFactory, anotherService) {
/** myFactory is the service that was setup
* by the config myFactoryProvider
*/
});
I've left out the implementations for myFactoryProvider and anotherServiceFactory so you look up angular docs to see what they're about.
The config and run describe the phases of an angular application. config phase is for setting up services and pipelines like the $http request pipeline and generally pre-app configuration.
The run phase is for when the app has loaded and you want to do something like load some user information from the api or anything else post-app load.
I personally used to use the config all the time with $stateProvider and things. But, I rarely used the run function.

AngularJS $provider and $injector and bootstrapping

I've been trying to get into the nitty-gritty with angular DI and really the bootstrap process at large, and I am a bit confused as to where things really happen. In my mind, the events are in this order.
App starts.
$provider registers service providers.
In the config phase, the providers can be configured.
Now is where I am lost.
The $injector, now having access to all the configured providers from $provide, calls the constructor functions (the $get function in each provider) to instantiate service instances.
Also, if that process is correct, how does the $injector handle cases where a service depends on another service?
Services are only instantiated at the moment when they are needed, rather than during Angular's initiation. For example, if you have a controller that isn't activated yet and it depends on services which haven't yet been used, those services will be instantiated and injected whenever that controller becomes active (like changing to a view that uses it). From then on, the same instance of each service will be used.
The same is true of services that depend on other services. All dependencies of anything are resolved before it is instantiated, so if a dependency has dependencies, the same process is applied (all of that dependency's dependencies will be instantiated first, and so on).
If a circular dependency is found (service foo has a dependency that depends on service foo), Angular will throw an exception and the functionality of those services will have to be refactored into different services that will not have this kind of circular chain.

The best way to Mock Services in AngularJS to decouple REST calls

I am building a web application with AngularJS and have built out a AngularJS Service that my controller uses to create, retrieve, update, delete data from. I want to farm out the UI work to another developer, but I don't want them to require the dependencies that the AngularJS Service does. I'd like to build a Mock Service and just return mock data rather than call the live REST service.
My App is set up like this:
var myApp = angular.module('myApp', ['ui.bootstrap']);
My Controller currently hooks up to the Service using:
myApp.controller('TodoCtrl', function TodoCtrl($scope, $JSOMService) {
$JSOMService.DoStuff();
});
And my Service is defined like this
myApp.service('$JSOMService', function ($q, $http) { ... });
What the are best ways to handle switching out the service for another one? This is a little different from Unit Testing and I wondered if there are any common ways of doing this?
For now I'm just having a slightly different code base where I just switch out the Angularjs Service javascript files loaded to handle this.
You can get access directly to the provider service, which controls injections. It would look something like this, where $provide is injected:
$provide.value('$JSOMService', MockJSOMService());
This will basically say, whenever someone asks for $JSOMService, give them whatever was returned from MockJSOMService.
You can set this up at the when you set up your app. Like this:
myApp.run(['$provide', function($provide) {
$provide.value('$JSOMService', MockJSOMService());
}]);
This is basically how you could switch out services. Admittedly a little funky, but I hope this helps!

Resources