I have a global module 'app' which include two other modules 'app.core' and 'app.service'. This is basic settings. Inside the two sub module I can access the constant from both two modules. However I cannot access constants declared in 'app' within 'app.core' or 'app.service'.
Also I use angular.bootstrap to delay my initialization because I need to retrieve a config from server. But after receiving the config I do angular.module('app').constant('config', config); So the constant should be well defined..
LAst point, inside the 'app' module config I can access the config constant.
Any idea ?
'app' module constants declaration
angular.module('app',[
'app.core',
'app.service'
])
.module('app')
.constant('foo', 'foo')
'app.core' constants
angular
.module('app.core')
.constant('core', 'core');
In 'app.service' I can get core constant
angular.module('app.services',[]).config(function($injector){
console.log($injector.get('core'));
})
But I cannot retrieve 'app' constant
angular.module('app.services',[]).config(function($injector){
console.log($injector.get('foo'));
})
Will crash
In both configurations you are trying to access a constant defined within a separate module, but then not defining that module as a dependency. How, for example, can app.services have access to foo when foo is defined on a module which requires app.services in the first place?
The reason that core is available to app.services despite this is because you have listed the dependencies in such an order when defining app that angular happens to have loaded app.core prior to app.services. The order of the defined dependencies, however, should not matter.
In order to correct this, you should consider refactoring your modules so that there is no inherent circular dependency. For example, consider making your config a module in itself and inject it into the dependent services:
angular.module('app', ['app.core', 'app.services'])
angular.module('app.config', [])
.constant('foo', 'foo')
angular.module('app.core', ['app.config'])
.config(function(foo) {})
angular.module('app.services', ['app.config'])
.config(function(foo) {})
Note also that using the injector to get constants is un-necessary as they can be injected directly during the configuration stage.
Related
angular
.module('password_forgot', ['app.auth'])
.controller('password_forgot', main);
main.$inject = ['auth'];
function main(auth) {
auth.sendEmail().then(function(){
//blablabla
});
}
Should I inject MyFactories 3 times? What is the best practice for doing this?
You're confusing three very different things:
a module: that's where various components (controllers, services, directives, filters) are registered. Modules can depend on other modules. In your example, you're defining a module named 'password_forgot' which depends on a module named 'MyFactories'. Your application is a module that depends on other modules, that depend on other modules, etc. The application is thus the union of all the components registered in all those modules. Note that factories are not angular components. A module named 'MyFactories' should probably rather be named 'MyServices'.
a service: that's an angular component that can be injected in other angular components. In your example, you're injecting a service named 'MyFactories' into the controller 'main'. It's very unusual, and probably an error, to name a service the same way as you name a module. You shouldn't do that. The name 'MyFactories' is a pretty bad name for a service. A service should have a specific responsibility, like 'translate', or 'authentication' or 'products'
a factory: a factory is a function that is registered into a module under a given name ('authentication', for example), and whose responsibility is to create and return the unique instance of the service of that name ('authentication'). This function is called once by angular, and the returned object or function is the service that is injected in the other components.
The line
main.$inject = ['MyFactories', '$scope'];
is only necessary if you minify your JS code. If you plan to do that, then I advise you to avoid inserting such a line of code by yourself, and to rely on ng-annotate, before the minification, to modify your code in order for it to be minifiable.
Testing modules dependent on other modules, and providers and services in them - possible. Let's say we need to test module 'A' that depends on module 'B':
totally not coffeescript, rather pseudo code for brevity :)
beforeEach ->
angular.module('B',[])
module ($provide)->
$provide.provider 'foo', ->
this.$get = -> # ...etc...etc.
it 'testing module A', ->
module('A')
inject -> # ...etc...etc.
Now, imagine we have quite a few modules that need to be mocked, and many services and providers. So how would you move faking 'B' part into a separate file?
and how they'd be used after? It's quite possible to use module loaders (webpack, browserify) but how would one do without? Using angular capabilities and what Karma offers out of the box?
Is it possible to wrap entire mock into an angular module and load it in beforeEach?
Yes, actually, making services their own modules can easily be tested in Angular using Karma.
As long as you have your testing framework set up properly—Angular mocks, and all of the required modules are loaded into Karma (check the Karma config)—then this is fairly straight-forward.
The below example assumes FirstService is the service you are testing, and SecondService is another service that FirstService depends on and interacts with.
describe('FirstService', function () {
var SecondService;
beforeEach(module('FirstService'));
beforeEach(inject(function (_SecondService_) {
SecondService = _SecondService_;
spyOn(SecondService, 'doSomethingElse');
}));
it('calls SecondService.doSomethingElse', function () {
FirstService.doSomething();
expect(SecondService.doSomethingElse).toHaveBeenCalled();
});
});
You include the module in a beforeEach as specified on the first line—do not use square brackets [] when including it as that redefines the module and you want to include the existing module as-is.
Although you should omit the square brackets when importing modules, you should be sure that when you are initially creating your FirstService module (e.g., in your app.js file, if that's how your project is set up) that you are indeed including SecondService as a dependency:
angular.module('FirstService', ['SecondService'])
The second beforeEach is injecting the SecondService module. It is already globally available to be injected and can be accessed by its module name with a leading and trailing underscore, i.e. _SecondService_. I've assigned it to a variable so that it is available to be used in the it statements, otherwise I would need to inject it in each it statement instead of a beforeEach.
I am creating a spy using Jasmine's spyOn to spy on the method doSomethingElse to ensure it has been called. The second parameter of spyOn (the method name) should have quotes but the first should not—it should be the variable you just defined for your SecondService.
The example unit test tests that calling FirstService.doSomething() calls SecondService.doSomethingElse().
Please note this is not a duplicate of:How to inject module and make it accesible to entrie angular app
I have a module (app.config) that I would like to inject into my entire app.
The module needs to be accessible within all other modules and submodules(modules inside modules) injected into myApp
For example, my app looks like this:
angular.module('myApp', [
'app.config',
'module#1',
'module#2',
'module#3',
'module#4'
])
.config...
/////////////////////////////////
Here's app.config
angular.module('app.config', []).
constant('NAME1', 'Name1').
constant('NAME2', 'Name2');
////////////////////
I want 'app.config' injected in such a way that it should be accesible in module#500 which is not directly a dependecy of 'myApp', but a dependecy of module#1 - which, in turn, is a dependecy of myApp.
module#1 is defined as such (as shown,module#500 is a dependecy of module#1):
angular.module('module#1', [
'module#500',
'module#501',
'module#502',
...
]);
Here's my problem:
angular.module('module#500', []).
service('serviceOne', serviceOne);
function ServiceOne($http, NAME1) {
var service = {
getMyProfile: function(){return $http.get('api/' + NAME1);}
};
return service;
}
Problem - I get an error-> Uncaught Error: [$injector:unpr] Unknown provider: NAME1Provider <-NAME1 <- serviceOne But I thought I injected it to the entire app???
I don't want to add module#500 directly as dependency to 'myApp' I wan't to leave module#500 as a dependecy of module#1. And I want to leave module#1 as a dependency of myApp
I don't want to individually inject app.config to each and every module either. Any other solutions?
I don't want to individually inject app.config to each and every
module either. Any other solutions?
I don't know what solution you could be expecting? With this line: angular.module('module#500', []). how is module#500 going to know about anything else, it has nothing given to it. Maybe I'm misunderstanding something here.
edit, as I've just read your post and comments from yesterday: I think you're not understanding dependency-injection properly. It only goes one way, module#1 has access to module#500, but module#500 doesn't have access to module#1. It has to be that way: How could you unit-test module#500 if it has some behavior that depends on module#1, which is not mentioned anywhere in its code?
I'm guessing your project doesn't really call for so many modules, if they all depend on the same config variables. Those factories and services that depend on the config variables should be in the same module with it. That is proper compartmentalization and will make your life easier in the long run.
If you don't want to make myApp dependent on app.config (though it would be the right thing to do because its submodules depend on it), you can load config module with manual bootstrapping
angular.bootstrap(document, ['myApp', 'app.config']);
instead of ng-app="myApp".
You forgot to inject NAME1 in your ServiceOne service.
eg: function ServiceOne($http, NAME1)
I'm using AngularJS and Django together, so I apply the recipe found here to reconfigure Angular's template delimiters:
angular.module("some module name").config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
I have to re-apply this piece to each module, only changing the "some module name" string. Is there a way to define this piece of configuration once, and then cleanly require and inject it into a module at the module's definition site?
Since $interpolateProvider is a provider, any change to it will affect not only the current module but also all the modules that depends on it.
You can, for example, define a "config" module and add it as dependency to other modules:
angular.module('config', []).config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
And on the other modules:
angular.module('module1', ['config']);
angular.module('module2', ['config']);
Demo plunker
In all the tutorials and examples I've read regarding Angularjs, they all define modules with an empty list as the second parameter:
angular.module('myModule', []);
I understand that the presence of the second parameter is required to create a new module, but I don't understand why there are never any elements in the list. The document for angular.module says nothing about what the contents of the list would represent.
However, from defining my app module I understand the list represents modules upon which the new module depends - so why is it always empty for app sub-modules? For example, in my own project I've got a users module upon which my app depends:
/* app.js */
angular.module('myApp', [
'ngCookies',
'ngResource',
'ui.bootstrap',
'ui.router',
'myApp.system',
'myApp.users'
]);
angular.module('myApp.system', []);
angular.module('myApp.users', []);
When I finally got around to learning how to unit test using karma and jasmine, I spent hours trying to figure out this error message:
Error: [$injector:modulerr] Failed to instantiate module myApp.users due to:
Error: [$injector:unpr] Unknown provider: $stateProvider
http://errors.angularjs.org/1.2.13/$injector/unpr?p0=%24stateProvider
at /Users/matt/Development/myApp/public/lib/angular/angular.js:3556
at getService (/Users/matt/Development/myApp/public/lib/angular/angular.js:3683)
at invoke (/Users/matt/Development/myApp/public/lib/angular/angular.js:3710)
at /Users/matt/myApp/public/lib/angular/angular.js:3639
Eventually I found two things that would fix this problem - either I could load module dependencies in the test code, or I could add the dependencies to the empty list in the users module declaration:
/* UserControllerTest.js */
describe('UserCtrl', function () {
var $rootScope,
$scope,
controller;
beforeEach(function () {
module('ui.router');
module('myApp.system');
module('ngResource');
module('myApp.users');
inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
controller = $injector.get('$controller')('UserCtrl', {$scope: $scope});
});
});
it('should work', function () {
expect(true).toBe(true);
});
});
Or:
/* app.js */
...
angular.module('myApp.users', [
'ngResource',
'ui.router',
'mean.system'
]);
Is there some reason I wouldn't want to do the latter? Why is it that I never see that in docs and tutorials - would it prevent me from being able to mock those dependencies in testing?
Why is it that I don't need the latter sub-module definition for regular operation of my app? I do have a series of 'injection locals' specified for UserCtrl - why isn't that sufficient for the unit test?
The purpose of modules is to encapsulate self-contained parts of code (e.g. a reusable widget, code tgst implements a specific feature etc). In general, it is a good practice to have each module declare the dependencies it relies upon.
If not, then the module relies on the modules thst require it to declare those dependencies, which messes "self-containment", hurts testability and reusability and introduces a bunch if potential future bugs.
That said, there doesn't seem to be a reason not to declare dependencies with each module. (And no, it will not prevent you from mocking the dependencies in unit tests.) Of cource, as one would expect, each module is loaded once even if it is required by several modules.
The API reference is indeed not very detailed about angular.module, but the Developer Guide has a more extensive description.
E.g., quoting the "Dependencies" section:
Modules can list other modules as their dependencies. Depending on a module implies that required module needs to be loaded before the requiring module is loaded. In other words the configuration blocks of the required modules execute before the configuration blocks of the requiring module. The same is true for the run blocks. Each module can only be loaded once, even if multiple other modules require it.