AngularJS: Defining module dependency at run-time, dynamically - javascript

Let's say I have a simple module:
angular.module('Platform', []);
And let's say I have another module:
angular.module('PlatformProductA', []);
Is there an Angular function I can call from JavaScript that will inject module 'PlatformProductA' as a dependency into module 'Platform', as if module 'Platform' had been defined like this:
angular.module('Platform', ['PlatformProductA']);
In other words, after a module has been defined, I need to inject another module into it as a dependency.
EDIT:
I'm not sure how, but this seems to work:
angular.module('Platform', []);
angular.module('PlatformProductA', []);
angular.module('Platform').requires.push('PlatformProductA');
Angular somehow sees that 'PlatformProductA' has been injected?

Yes there is $injector, which allows dynamic injection
angular.module('Platform', [$injector], function(){ // don't remember exact syntax
$scope.myModule = $injector.get('PlatformProductA');
});

Related

Mocking module dependencies in AngularJS unit tests

I have an angular 1.5 project with many modules and each module may depend on other modules. Trying to unit test say a controller which is part of a module I would do import the module like this:
angular.mock.module('SaidModule');
...then provide and inject its services where needed.
The problem is that SaidModule depends on AnotherModule1, AnotherModule2, AnotherModule3....
angular.module('SaidModule', ['AnotherModule1', 'AnotherModule2', 'AnotherModule3']);
So naturally when I call SaidModule the other modules are also invoked which is out of scope in terms of Unit testing
In the unit test I have tried the following solution
angular.module('AnotherModule1',[]);
angular.module('AnotherModule2',[]);
angular.module('AnotherModule3',[]);
angular.mock.module('SaidModule');
and although for the current unit test I have successfully decoupled the dependencies I have also destroyed
the actual AnotherModule1, AnotherModule2, AnotherModule3 so when its there turn to be unit tested they are
not even visible in the angular project which seems correct to me. as I am using angular.module to define a
new module which just happens to override the actual module.
This solution though is also suggested here mocking module dependencies
In the angular docs it states see angular docs mock module
If an object literal is passed each key-value pair will be registered on the module via $provide.value,
the key being the string name (or token) to associate with the value on the injector.
So it seems to me that the solution is using somehow angular.mock.module somehow to override the dependent
modules but so far I have not found a solution.
Any help much appreciated
By calling angular.module('AnotherModule1',[]) you are redefining the AnotherModule1, which I think is causing your downstream problems. Instead, use $provide for each dependent service. There's no need to mock the dependent modules.
Let's say your controller definition looks like this:
angular
.module('SaidModule', ['AnotherModule1', 'AnotherModule2'])
.controller('SaidController', [
'$scope',
'AnotherService',
function($scope, AnotherService) {
this.anotherService = AnotherService.helper();
}
);
Then your test might look like:
describe('SaidController', function() {
var controller, scope, AnotherService;
beforeEach(module('SaidModule'));
beforeEach(module(function($provide) {
AnotherService = { helper: function() { return 0; } };
$provide.value('AnotherService', AnotherService);
}));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
controller = $controller('SaidController', {
$scope: scope
});
}));
it('creates controller', function() {
expect(controller).not.toBeNull();
});
});
There's no need to mock the dependent modules, just the dependent services.

How to define controllers in separate files

Suppose that I have an SPA written via AngularJS 1.x.
It has a single app module defined like this:
var app = angular.module('app', ['ngAlertify', 'ngRoute', 'ui.bootstrap'])
I also have several controllers which are defined in separate *Ctrl.js-files. What is the appropriate way to define them?
I see two options here. The first one is
app.controller('LoginCtrl', function($scope) { /* ... */ });
and the second one is
angular.module('app').controller('LoginCtrl', function($scope) { /* ... */ });
Which one is better and most common-used practice? Is there any downsides of using either of them?
If I understand your question correctly then you wan to know different between
app.controller('LoginCtrl', function($scope) { /* ... */ });
vs
angular.module('app').controller('LoginCtrl', function($scope) { /* ... */ });
In above two in first method app is a global object which you declared somewhere i.e. in app.js like
var app = angular.module('app',[]);
In this case app is a global variable which will be accessible throughout your entire application. which I believe is not a good thing to use global variable
in our application.
In second method we are using global angular object to create a controller so that in this we will not be using global variable. In this case app.js will look like
(function(){
'use strict';
var app = angular.module('app', []);
....
....
....
....
})
In this case app variable will not be available anywhere apart from this file.
So I belive second method is better than first one.
My personal preference is to use app.controller('LoginCtrl', function($scope) { /* ... */ }); as this makes it easier to reuse the controller in another project with little to no changes, without those annoying module not found errors because you forgot to rename the module when reusing the file
I think that depends a bit on the personal style of writing. One thing is that while working with AngularJS 1.x.x you can have different styles of writing code, method stacking etc.
Personally, I prefer app.controller('LoginCtrl', function($scope) { /* ... */ }); mainly because you can easily preview your controller and distinguish it from thge module. Another bonus I see of having a clearly defined separate module is that you can easily check what includes you have ('ngAlertify', 'ngRoute', 'ui.bootstrap').
Most commonly used as far I have seen, even here on SO, is the method that I previously mentioned. Yet again this is something that is more reflective of personal style rather than strong pre-requirements of writing code. I hope that helps to some extend.
None of the above. The purpose of modules is to keep the application modular and not pollute global scope.
You can have var app = ... but this should be done inside IIFE once per file.
Another issue with modules is the precedence. If the application uses angular.module('app') module getter, the files should be loaded in specific order, in order for the module to be defined when it is retrieved in other files. This creates problems if they aren't, for example when they are concatenated in alphabetic order.
The solution is to use one module per file. This makes the application truly modular, independent of file loading order, also benefits testability. See also this answer for how this pattern supposed to work.
You can use module setter and getter methods for implementation of controllers in different file.
Suppose myApp.module.js
angular.module('myApp', []); //Setter method, registring module
In myApp.homeCtrl.js
var myApp = angular.module('myApp'); // getter method, getting the module already registered.
myApp.controller('homeCtrl', [function()]{ })
For more info check this https://toddmotto.com/angular-modules-setters-getters/
The second approach your are taking about is better because it uses the already created module and doesn't create the new module but with the first approach you are using global variable that is not recommended

Dependency Injecting an empty module into a controller in Angular

I am trying to dependency inject a module into another—the former is simply an empty module.
angular.module('module1', []);
angular.module('module2', [])
.controller('Module2Ctrl', ['module1', '$scope', function (module1, $scope) {
$scope.expression = 'hello!';
}]);
HTML:
<html ng-app="module2">
<body ng-controller="Module2Ctrl">
<h1>{{expression}}</h1>
</body>
</html>
I'm getting the dreaded Unknown provider: module1Provider <- module1 <- Module2Ctrl message.
What's going on? I believe everything is defined as it should be—though module1 has no definitions, I can't find information anywhere on what would stop this from working.
Plnkr: http://plnkr.co/edit/goMVFRNuPgG6iIpGYI1Y?p=preview
Thanks :-)
You inject providers (such as factories, services, ...), not modules. Remove the module1 injection and it will work. What you're thinking of doing is probably declaring module2 as a module dependency of module1:
angular.module('module1', ['module2']);
and then ng-app="module".
angular.module can not injected inside a controller,Only one angular.module can be injected inside another module.
angular.module('module2', ['module1'])
You should never do that angular. Only angular components are inject-able like service,controller, factory, filter, provider,etc.
For initializing angular on page you could do angular.bootstrap
angular.bootstrap(document,["module2"])

How does angular resolve controllers defined in modules?

Looking at the angular documentation for controller there's something that confuses me. Let's say someone does this:
angular.module('foo').controller('controller1', ['$scope', function($scope) {
});
and:
<div ng-app='app' ng-controller='controller1'>
</div>
How does angular know that 'controller1' resides in module 'foo'?
And furthermore if someone does this in addition:
angular.module('bar').controller('controller1', ['$scope', function($scope) {
});
Which one will angular chose now?
When you declare ng-app here:
<div ng-app='app' ng-controller='controller1'>
</div>
Angular will look for a module definition with the name app. Something like this:
angular.module('app', []); // notice the []
Inside the second parameter [] array, Angular wants the dependent modules. So, in order to include controller1 from foo module, you would do this:
angular.module('app', ['foo']);
And from the bar module:
angular.module('app', ['bar']);
In each of these, there is only a single controller1 named controller.
So, what happens when you register both the foo and bar modules? I would think that the last one wins. So, if you define app to be:
angular.module('app', ['foo', 'bar']);
Then, the bar module's controller1 will overwrite the name controller1 inside the injector.
So, there is no built-in mechanism that allows for the same name to be applied across modules. Because of this, you can employ naming schemes to make sure that the controller is unique:
angular.module('bar').controller('bar.controller1', ['$scope', function($scope) {
});
How does angular know that 'controller1' resides in module 'foo'?
At the time that Angular is resolving controller1, it doesn't know what module it is in. Once the module is a dependent (on the module defined in ng-app), all of the controllers (in all modules) are only resolved off of their names.
Angular doesn't really know where the controller comes from. Your application includes either foo or bar and then controller1 is registered.
If you include both foo and bar controller1 is from whatever module you included last.

Use underscore inside Angular controllers

How do I use underscore library inside angularjs controllers?
On this post: AngularJS limitTo by last 2 records
somebody suggested to assign an _ variable to the rootScope so that the library will be available to all the scopes within the app.
But I'm not clear where to do it. I mean should it go on the app module declaration? i.e:
var myapp = angular.module('offersApp', [])
.config(['$rootScope', function($rootScope) { }
But then where do I load underscore lib? I just have on my index page the ng-app directive and script reference to both the angular-js and underscore libs?
index.html:
<head>
</head>
<body ng-app="offersApp">
...
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="scripts/vendor/angular.js"></script>
<script src="scripts/vendor/underscore.js"></script>
...
How do I achieve this?
When you include Underscore, it attaches itself to the window object, and so is available globally.
So you can use it from Angular code as-is.
You can also wrap it up in a service or a factory, if you'd like it to be injected:
var underscore = angular.module('underscore', []);
underscore.factory('_', ['$window', function($window) {
return $window._; // assumes underscore has already been loaded on the page
}]);
And then you can ask for the _ in your app's module:
// Declare it as a dependency of your module
var app = angular.module('app', ['underscore']);
// And then inject it where you need it
app.controller('Ctrl', function($scope, _) {
// do stuff
});
I have implemented #satchmorun's suggestion here:
https://github.com/andresesfm/angular-underscore-module
To use it:
Make sure you have included underscore.js in your project
<script src="bower_components/underscore/underscore.js">
Get it:
bower install angular-underscore-module
Add angular-underscore-module.js to your main file (index.html)
<script src="bower_components/angular-underscore-module/angular-underscore-module.js"></script>
Add the module as a dependency in your App definition
var myapp = angular.module('MyApp', ['underscore'])
To use, add as an injected dependency to your Controller/Service and it is ready to use
angular.module('MyApp').controller('MyCtrl', function ($scope, _) {
...
//Use underscore
_.each(...);
...
I use this:
var myapp = angular.module('myApp', [])
// allow DI for use in controllers, unit tests
.constant('_', window._)
// use in views, ng-repeat="x in _.range(3)"
.run(function ($rootScope) {
$rootScope._ = window._;
});
See https://github.com/angular/angular.js/wiki/Understanding-Dependency-Injection about halfway for some more info on run.
You can also take a look at this module for angular
https://github.com/floydsoft/angular-underscore
If you don't mind using lodash try out https://github.com/rockabox/ng-lodash it wraps lodash completely so it is the only dependency and you don't need to load any other script files such as lodash.
Lodash is completely off of the window scope and no "hoping" that it's been loaded prior to your module.
you can use this module -> https://github.com/jiahut/ng.lodash
this is for lodash so does underscore

Categories