Dependencies Between Modules - Controllers? - angularjs

This tells AngularJS that all values, factories and services defined inside the myUtilModule should be available inside the myOtherModule module too. In other words, myOtherModule depends on myUtilModule.
var myUtilModule = angular.module("myUtilModule", []);
myUtilModule.value ("myValue" , "12345");
var myOtherModule = angular.module("myOtherModule", ['myUtilModule']);
myOtherModule.controller("MyController", function($scope, myValue) {
});
My question is: What if you define controllers inside myUtilModule and try to use them on myOtherModule. Is this possible?

Yes, controllers will also be available between modules, a pratical example could be as follows:
angular.module('test.controllers').controller('HomeCtrl');
and then on your main module:
angular.module('test', [
'test.controllers',
'test.services',
...
];
This helps a lot on unit testing and reusability of packages.

Related

Angular 1: $injector can't find a provider dependency when injecting provider inside a provider

My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo

Mock an AngularJS module to pass to another

I'm having some trouble mocking a factory belonging to one of my modules. The factory I would like to mock has 2 dependencies:
Factory class:
angular.module('serviceapp')
.factory('claims.service', ['applicationSettings', 'localStorageService', function (applicationSettings, localStorageService) {
//Factory code here
}]);
Test class:
//Instantiate some values
var mockAppSettings = {};
var mockStorageService = {};
var $factory; //Will hold my factory
//Targeting my module for mocking
beforeEach(angular.mock.module('serviceapp'));
//Providing some values for the dependencies of my module
beforeEach(module('serviceapp', function ($provide) {
$provide.value('applicationSettings', mockAppSettings);
$provide.value('localStorageService', mockStorageService);
}));
//Problems start here
beforeEach(inject(function ($injector) {
$factory = $injector.get('claims.service');
}));
I get an error message
Failed to instantiate module serviceapp due to:
Failed to instantiate module accountModule due to:
Module 'accountModule' is not available!
When investigating I see that accountModule is listed as a dependency for the serviceApp module.
App.module class:
angular.module('serviceapp', [accountModule])
However I'm having some trouble mocking this module to pass to serviceapp. I have tried to mock the accountModule in the same way I have mocked the serviceapp in the beginning however this is still bring up the same error message. How can I mock and pass one module to another?
angular.mock.module('serviceapp') shouldn't be read literally. It doesn't mock a module. It is the same thing as module('serviceapp') and is used in modular environments where module is reserved.
So, all that
beforeEach(angular.mock.module('serviceapp'));
beforeEach(module('serviceapp', ...));
does is loading serviceapp twice (doesn't hurt but doesn't help either).
To avoid Module 'accountModule' is not available!, it should be (re)defined:
beforeAll(() => {
angular.module('accountModule', [])
});
The problem with this approach is that even if it was defined, it will be overridden to the end of test run. If real accountModule needs to be used in other tests, this won't be possible.
The appropriate solution for similar design issues (this also applies to dependencies that aren't desirable in tests, e.g. router modules) is
angular.module('serviceapp', ['accountModule']);
angular.module('serviceapp.internal', [])
.factory('claims.service',...);
Here serviceapp serves as a shallow wrapper for serviceapp.internal, while the latter can be safely tested. If serviceapp is top-level module that is used for bootstrapping, this indicates that the application wasn't modularized enough, this hurts testing.

Angular - Issue in moving service into separate file

I have a service with a few methods:
function userService($http, API, auth) {
....
}
and then used in my module like:
var app = angular.module('app', ['ngRoute'])
.service('user', userService)
...
All of this is in my app.js file, I want to separate things so its easier to maintain. I'm trying to use the line .service('user', '/services/userService') but its not working any ideas how to i need to reference this?
Thanks.
I think you are creating a new module instead of use yours.
To retrieve an existing module and use it in a separated file, you have to do :
var app = angular.module('app')
.service('user', userService')
// ...
Instead of
var app = angular.module('app', ['ngRoute'])
.service('user', userService')
// ...
Documentation available at https://docs.angularjs.org/guide/module#creation-versus-retrieval
EDIT from #koox00 answer
Don't forgot to load all files related to your module in your markup, in the good order (before load the file containing your module declaration).

Angular IoC best practice

What's the best practice in AngularJS when one needs to use a different implementation of a service/factory depending on the environment.
Let's say I have a service MessageService, which is injecting some other service, but based on the device/platform, it should use WebService vs. MobileService.
Here's what I do now:
angular
.module('message')
.service('MessageService', messageService);
var service = 'WebService';
if (mobileDevice) {
service = 'MobileService';
}
messageService.$inject = [service];
function messageService(service) {
service.call(); // use the shared interface for both services inside this service
}
Is there a better - more elegant - way to do this?
You can use the module's 'config' block to do this using a 'provider' like here -
angular.module('app').config(['$provide', '$injector', 'mobileDevice',
function($provide, $injector, mobileDevice) {
$provide.value('MessageService', (mobileDevice) ? $injector.get('MobileService') : $injector.get('WebService'));
}
]);
This is assuming that 'mobileDevice' is an angular constant.
Now you can inject 'MessageService' to do what you wanted.

How to mock Angular modules before injecting?

I have the following dependencies in my application:
angular.module 'MyApplication.NetworkingModule', ['ngResource']
.value 'NetworkingValues'
angular.module 'MyApplication.MyModule', []
.factory 'MySpecificResource', ['NetworkingValues', ...]
.factory 'MyService', ['MySpecificResource', ...]
So MySpecificResource depends on NetworkingValues.
The thing is I'd like to mock some of these values before using them in resources in tests.
Doing this:
beforeEach module 'MyApplication.NetworkingModule'
beforeEach module 'MyApplication.MyModule'
causes that factory is invoked immediately and I don't have a chance to replace some of NetworkingValues.
beforeEach inject (#MyService, #NetworkingValues) ->
MyService depends on MySpecificResource, which depends on NetworkingValues that I want to stub before tests.
What are the correct way to do that?
Have you tried something along these lines?
var mockedValues;
beforeEach(module(function($provide) {
$provide.value('NetworkingValues',mockedValues);
}));
beforeEach(inject(function(_NetworkingValues_) {
mockedValues = {....};
}));
The $provide will supply your mockedValues instead of the actual NetworkingValues.
So my final solution was to provide constant and inject it together with module function:
beforeEach module "MyApplication.NetworkingModule", ($provide) ->
$provide.constant 'NetworkingValues', endpointGet: 'my-endpoint/:id'

Resources