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:
- The presence of this code does not change, alter, affect regular angularjs users in any way
- The code is already in angular js - i'm just exposing it through an accessible point
- It will enable other library writers to be able to extend angularjs more easily and do cool stuff
- It allows angular to be more modular and scalable - using bandwidth more intelligently
- 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?