88

Is there a way to inject a late dependency to an already bootstrapped angular module? Here's what I mean:

Say that I have a site-wide angular app, defined as:

// in app.js
var App = angular.module("App", []);

And in every page:

<html ng-app="App">

Later on, I'm reopening the app to add logic based on the needs of the current page:

// in reports.js
var App = angular.module("App")
App.controller("ReportsController", ['$scope', function($scope) {
  // .. reports controller code
}])

Now, say that one of those on-demand bits of logic also requires their own dependencies (like ngTouch, ngAnimate, ngResource, etc). How can I attach them to the base App? This doesn't seem to work:

// in reports.js
var App = angular.module("App", ['ui.event', 'ngResource']); // <-- raise error when App was already bootstrapped

I realize I can do everything in advance, i.e -

// in app.js
var App = angular.module("App", ['ui.event', 'ngResource', 'ngAnimate', ...]);

Or define every module on its own and then inject everything into the main app (see here for more):

// in reports.js
angular.module("Reports", ['ui.event', 'ngResource'])
.controller("ReportsController", ['$scope', function($scope) {
  // .. reports controller code
}])

// in home.js
angular.module("Home", ['ngAnimate'])
.controller("HomeController", ['$scope', '$http', function($scope, $http){
  // ...
}])

// in app.js, loaded last into the page (different for every page that varies in dependencies)
var App = angular.module("App", ['Reports', 'Home'])

But this will require I initialize the App everytime with the current page's dependencies.

I prefer to include the basic app.js in every page and simply introduce the required extensions to each page (reports.js, home.js, etc), without having to revise the bootstrapping logic everytime I add or remove something.

Is there a way to introduce dependencies when the App is already bootstrapped? What is considered the idiomatic way (or ways) to do this? I'm leaning towards the latter solution, but wanted to see if the way I described could also be done. thanks.

Rahil Wazir
  • 10,007
  • 11
  • 42
  • 64
sa125
  • 28,121
  • 38
  • 111
  • 153
  • Can you give me your final working code, to have a look at ? – Mangu Singh Rajpurohit Dec 22 '15 at 02:04
  • @user2393267 mmm, been so long, I have no idea if I still have that code... Sorry. I think I eventually revisited the design, because usually when I have to hack through things it means I'm not doing it right... However, if you're still determined to pursue this approach, the [answer below](http://stackoverflow.com/a/28315655/187907) seems like a good lead. – sa125 Dec 22 '15 at 09:26

6 Answers6

114

I solved it like this:

reference the app again:

var app = angular.module('app');

then push your new requirements to the requirements array:

app.requires.push('newDependency');
AlBaraa Sh
  • 2,202
  • 3
  • 20
  • 29
  • 3
    This works for me +1 I used this for multiple dependency al like app.requires.push('doctorCtrl', 'doctorService'); – Amir Jun 21 '15 at 10:48
  • 8
    This saved my architecture. – Saeed Neamati Sep 06 '15 at 09:50
  • 3
    It's not working for me. I'm adding the dependency dynamically as described, but Angular still can't find a service from the added module. – Slava Fomin II Sep 16 '15 at 12:35
  • 6
    And `app.requires` is an array, so if you're adding **multiple** dependencies (as in your Reports example), you pass them as separate arguments to the [`push`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) method: `app.requires.push('ui.event', 'ngResource');` or if your additional dependencies are already an array: `Array.prototype.push.apply(app.requires, ['ui.event', 'ngResource'])` – Nate Anderson Oct 06 '15 at 20:35
  • 2
    I sometimes skip up voting because I don't want to login, not this time! Thanks! – CularBytes Aug 10 '16 at 10:41
  • It works for me partially: it loads the new module but not the associated controllers. So it behaves as it was not loaded. Any advice? The Error is always the same: [ng:areq] – Simona Adriani Sep 01 '16 at 12:55
  • If an app is already bootstrapped (as suggested in the title of this question), then pushing into the requires array won't change anything. Consider [this JSFiddle example](https://jsfiddle.net/theredpea/4pL59xyk/1/); if you `bootstrap(...)` before `requires.push(...)`, then the `` directive won't compile/render. If you bootstrap **after**, the `` directive will compile/render. Use of ng-app may delay bootstrapping until later than the OP thought; that's why my example manually bootstraps. – Nate Anderson Jun 27 '17 at 03:51
12

Simple... Get an instance of the module using the getter like this: var app = angular.module("App");

Then add to the "requires" collection like this: app.requires[app.requires.length] = "ngResource";

Anyway, this worked for me. GOOD LUCK!

David Donovan
  • 121
  • 1
  • 2
9

According to this proposal on the Angular JS google group this functionality does not exist as of this moment. Hopefully the core team decides to add this functionality, could use it myself.

Anvaka
  • 15,658
  • 2
  • 47
  • 56
Matt Nibecker
  • 11,487
  • 2
  • 14
  • 9
4

If you wish to add multiple dependencies at once, you can pass them in push as follows:

<script>
    var app = angular.module('appName');
    app.requires.push('dependencyCtrl1', 'dependencyService1');
</script>
fodma1
  • 3,485
  • 1
  • 29
  • 49
Amir
  • 8,821
  • 7
  • 44
  • 48
4

I realize that this is an old question, however, no working answer has yet been provided, so I decided to share how I solved it.

The solution requires forking Angular, so you can't use CDN anymore. However the modification is very small, so I am surprised why this feature doesn't exist in Angular.

I followed the link to google groups that was provided in one of the other answers to this question. There I found the following code, which solved the issue:

instanceInjector.loadNewModules = function (mods) {
  forEach(loadModules(mods), function(fn) { instanceInjector.invoke(fn || noop); });
};

When I added this code to line 4414 in the angular 1.5.0 source code (inside the createInjector function, before the return instanceInjector; statement), it enabled me to add dependencies after bootstrapping like this $injector.loadNewModules(['ngCookies']);.

Community
  • 1
  • 1
tjespe
  • 704
  • 7
  • 17
  • ? What's wrong with the [highest-voted answer (as of 6/26/2017)](https://stackoverflow.com/a/28315655/1175496) ? It looks like it was provided more than a year before you posted this ; does your answer have some advantage? – Nate Anderson Jun 27 '17 at 03:47
  • @TheRedPea Well there's nothing wrong with it, except that it didn't solve my problem, but this answer did – tjespe Jun 27 '17 at 05:55
  • And it didnt work for you because, I am guessing, you had already bootatrapped your app? I observed that too... – Nate Anderson Jun 27 '17 at 06:04
  • 2
    After reading the Google Community post linked, I'm even more sure that bootstrapping is the reason a solution like this is necessary. Upvoting because the original question asked about an "already bootstrapped application", which no one else addresses (indeed the OP may have misspoken) – Nate Anderson Jun 27 '17 at 12:19
1

Since version 1.6.7 it is now possible to lazy load modules after the app has been bootstrapped using $injector.loadNewModules([modules]). Below is an example taken from AngularJS documentation:

app.factory('loadModule', function($injector) {
   return function loadModule(moduleName, bundleUrl) {
     return getScript(bundleUrl).then(function() { $injector.loadNewModules([moduleName]); });
   };
})

Please read full documentation about loadNewModules as there are some gotchas around it.

There's also a very good sample app by omkadiri using it with ui-router.

Denis Pshenov
  • 11,157
  • 6
  • 42
  • 42