Proposal: Ability to add modules to AngularJS runtime AFTER bootstrap

10,565 views
Skip to first unread message

Craig Leyshan

unread,
Oct 24, 2013, 2:11:28 AM10/24/13
to ang...@googlegroups.com
Hi Everyone. I would like to propose a change to AngularJS that would make life easy for those people who wish to use something like requireJS to dynamically load angular modules after the angular application has been bootstrapped.  Just so people don't shoot this idea down as 'you dont need to do that', I'll explain my use case.  

Imagine a portal-like application where the user has a space where they can pull in tools, widgets, gadgets, portlets, doobiwhacckers (call them what you will).  Each of these "gadgets" is a simple angular module, complete with template, controller, services, directives etc.  Now imagine there are 30-50 of these available but each user only want say 2-5 on the screen at once.  Using angular as it is today, I need to download the code for all 50 gadgets........... and then bootstrap angular.  The majority of that code is not needed by the user so they are waiting longer than they should

Now you might say - cant each gadget be its own self-contained angular app that is bootstrapped separately?  It could.  but there is lots of shared code between these gadgets, and also shared state (ie services) and I want changes in one gadget to automatically be reflected in other gadgets using angular binding.  I also want the gadgets loosely coupled so that they dont need to be explicitly aware of the other gadgets - dependency injection in angular can take care of that for me so that each gadget has access to the same state.

So instead of lots of little angular apps, I need one angular app where I will use requireJS (for example) to only download the code for the gadgets that this user needs right now.  I can do that, for initial page load.  But what about when the user wishes to add a gadget to the page later?  At this point, I am stuck with angular - i would have to reload the whole page to get that to work.

Turns out there are only 3 lines of code needed to enable angular to register new modules after it has been bootstrapped. Here it is:

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

Put this code on about line 3005 in angularjs-1.2.0.rc2 (towards the end of the createInjector function, before the return statement) and voila - you have a new function you can call to notify angular that a new module has been loaded and it needs to be registered into angular's runtime.

This is what I do in my gadget app:
  • When a gadget is added, I first use requireJS to load the javascript that the gadget requires.  (you can use any script loader here, not just requireJS)
  • When the module code is loading it will be parsed and executed by the browser.  The code does things like angular.module('name of module here', [])  and angular.module('name of module here').service('someService,.....)  to define the new module and give it some controllers and services
  • when all the script for the new module have finished loading, I then call $injector.loadNewModules(['name of module here']).  This invokes the new code above.
  • Lastly, I do a $compile and $rootScope.$digest() to get the gadget moving
Why I think this should be added to angular:
  1. The presence of this code does not change, alter, affect regular angularjs users in any way
  2. The code is already in angular js - i'm just exposing it through an accessible point
  3. It will enable other library writers to be able to extend angularjs more easily and do cool stuff
  4. It allows angular to be more modular and scalable - using bandwidth more intelligently
  5. I've seen many forum posts raised by ppl wanting to be able to do this sort of thing.  I've seen some workaround suggested that work in some cases, but may break when ppl upgrade angular down the track.
Any one agree?  Disagree?

Grant Rettke

unread,
Oct 25, 2013, 3:06:36 PM10/25/13
to ang...@googlegroups.com
Sounds like some great functionality.
> --
> You received this message because you are subscribed to the Google Groups
> "AngularJS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to angular+u...@googlegroups.com.
> To post to this group, send email to ang...@googlegroups.com.
> Visit this group at http://groups.google.com/group/angular.
> For more options, visit https://groups.google.com/groups/opt_out.



--
Grant Rettke | ACM, AMA, COG, IEEE
gre...@acm.org | http://www.wisdomandwonder.com/
“Wisdom begins in wonder.” --Socrates
((λ (x) (x x)) (λ (x) (x x)))
“Life has become immeasurably better since I have been forced to stop
taking it seriously.” --Thompson

Craig Leyshan

unread,
Oct 28, 2013, 11:37:38 PM10/28/13
to ang...@googlegroups.com
Fork of angular.js created on GitHub for this change:  https://github.com/cleyshan/angular.js/tree/module-loading-after-bootstrap

Roland Zwaga

unread,
Oct 29, 2013, 2:50:41 AM10/29/13
to ang...@googlegroups.com
I just started my first large angular application, and this is the type of functionality that I will definitely need,
so, great initiative!

Roland

Daniel Tabuenca

unread,
Oct 29, 2013, 3:52:40 AM10/29/13
to ang...@googlegroups.com
Took a look at your changes. I never have analyzed the module configuration code, but I just imagined it would have been more complicated. Can you write some tests that test whether the injector can get a service from the newly added module?  I'm just curious if it was this simple, why would the angular team not have let people dynamically add modules from the get-go. Just wondering if that was actually a conscious decision, and if so, what the rationale for it was.


arush

unread,
Oct 29, 2013, 6:36:45 AM10/29/13
to ang...@googlegroups.com
Amazing work. I would love to see what the core team think of this

Craig Leyshan

unread,
Oct 29, 2013, 8:15:54 PM10/29/13
to ang...@googlegroups.com
Hi Daniel.  I have added more unit tests, and also added some to test controllers, directives and filters are all resolvable too.  I can imagine that the core team could probably do this in an even nicer way (perhaps marking modules dirty and invoking the loadNewModules function at the next $digest or something) to make it more transparent.  But the current form meets my immediate needs and has no impact on anyone using angular as it was originally intended.

Dario Gieselaar

unread,
Oct 30, 2013, 7:07:57 AM10/30/13
to ang...@googlegroups.com
Is there a way for me to get this working without modifying angular.js?

Robert Koritnik

unread,
Jan 7, 2014, 9:18:19 PM1/7/14
to ang...@googlegroups.com
Why did you decide to fork Angular code and not rather create a pull request on original repository?
Although I think it would be much nicer if we could call this instead:

angular.module("SomeModule").requires(["Module1", "Module2",...]);

It would make it more concise and easy to understand and use. It is part of module functionality so it would make much more sense to add an additional function to Module type.

Craig Leyshan

unread,
Jan 7, 2014, 10:02:28 PM1/7/14
to ang...@googlegroups.com
I forked initially as I was just playing with the idea and didnt really think it would end up as a pull request.

As to making the interface to this a little more intuitive, I agree it could be improved.  I was going for minimal change to angular, and also trying not to assume anything about how the new modules get loaded dynamically (requirejs, or something else).  I think alternative methods of invoking this could be done by calling the new method I have added to injector.  So almost treat the new method as a utility that can be used to build plugins or frameworks that provide a more integrated or seamless experience for the developer using a particular technology set (angular as far as i know cant load JS dynamically, it needs help from something like requirejs).  The underlying functionality already existed on injector and that is why I put the new function there.

Eduardo Marín Izquierdo

unread,
Feb 5, 2014, 10:37:47 AM2/5/14
to ang...@googlegroups.com
Hi!

This is a very interesting fork for AngularJS.

I am working currently with AngularJS 1.0.8.  That AngularJS version allowed inject a full module-directive as "widget" time longer the main application was iniziated. In fact, all this widgets are brought from severals urls provides for a widget service.

It was possible thanks for "angular.bootstrap" function focused to a particular DOM element.

But now, it is not possible. The $rootScope and particular widget $scope are not connected.

I am still styuding for a great solution without affect to the widgets.

(this is the current code working for me:)

try{
  var newElement = angular.element("<div " + angular.lowercase(scope.widgetinfo.moduleName) + " ></div>");
  element.append(newElement);
  angular.bootstrap(newElement,[angular.lowercase(scope.widgetinfo.moduleName)]);      
}catch(error){
  console.log(error);
}

Siva Thangeswaran

unread,
Feb 11, 2014, 2:25:27 AM2/11/14
to ang...@googlegroups.com
Craig, did you find any better way to do this by creating an external function or something?

I have tried to use this fork and works as expected and solves our problem without $compile and $rootScope.$digest(), is this something required?

Dean Kooiman

unread,
Mar 7, 2014, 12:30:24 AM3/7/14
to ang...@googlegroups.com
I have taken Craig's contributions a bit further.  I hope to be able to submit a pull request to the core team early next week.  I added angular.require and $injector.require functions.  The root $injector and angular functions will automatically compile and digest if a new module has been loaded into the run-time.


Jeremy Wells

unread,
May 9, 2014, 7:30:04 PM5/9/14
to ang...@googlegroups.com
Dean: did you submit a pull request? Is there a link to it?

Konstantin Isaev

unread,
Jun 9, 2014, 6:15:22 PM6/9/14
to ang...@googlegroups.com, gre...@acm.org
The integration process for the new module is not just register stuff the new module(s) provide.
Each module can have own 'config' and 'run' calls that must be processed.
Should Angular allow to override the injectables that are registered already? e. g replace a service? Even $http pr $compile? override a directive definition?
So, the Extension is not a just a Module. since the loading and bootstrap process is quite different here. 

Hunter Perrin

unread,
Nov 6, 2014, 3:51:12 PM11/6/14
to ang...@googlegroups.com
I had the same problem, and due to technical limitations, I couldn't alter Angular's source code. Here's my solution:

http://pinkieguy.com/post/101949656975/loading-module-dependencies-in-angular-after-bootstrap
Message has been deleted

Mahesh Thumar

unread,
Dec 31, 2015, 1:05:04 AM12/31/15
to AngularJS
Agree. It will be very helpful when loading a module asynchronously using requireJS.
Message has been deleted

Jakub Ludwig

unread,
Jan 19, 2016, 3:10:43 AM1/19/16
to AngularJS
Hi, great solution! Is there any way of integrating it into Angular without modifying the actual source code? I can always fork it, but that does not seems like a good way for me (I would like to update :))
Reply all
Reply to author
Forward
0 new messages