22

Thanks to the great article from Dan Wahlin, I managed to implement lazy loading of Angular's controllers and services. However, there does not seem to be a clean way to lazy load independent modules.

To better explain my question, assume that I have an app would be structure as below without RequireJS:

// Create independent module 'dataServices' module with 'Pictures' object
angular.module("dataServices", []).factory("Pictures", function (...) {...});

// Create 'webapp' ng-app, with dependency to 'dataServices', defining controllers
angular.module("webapp", ['dataServices'])
.controller("View1Controller", function (...) {...})
.controller("View2Controller", function (...) {...});

Here is the sample app with RequireJS in Plunker:
http://plnkr.co/aiarzVpMJchYPjFRrkwn

The core of the problem is that Angular does not allow adding dependency to ng-app post instantiation. As result, my solution is to use angular.injector to retrieve the instance of Picture object to be used in my View2Controller. See js/scripts/controllers/ctrl2.js file.

This creates 2 problems for me:

  1. The injected services runs outside of angular and therefore all async call must end with $scope.$apply()
  2. Messy code where some object can be injected using standard angular syntax while others require the explicit use of injector.

Have any of you figured out how to lazy load independent module using RequireJS and somehow hook this module in angular so normal angular dependency injection syntax can be used?

Note:
The question is on lazy loading of independent module. One simple solution to this specific example is to create "Pictures" object using cached $providers during ng-app.config but that is not what I am looking for. I am looking for solution that works with 3rd party module such as angular-resource.

marcoseu
  • 3,892
  • 2
  • 16
  • 35
  • i discovered this [solution][1] with jquery and resolve of $routeProvider [1]: http://stackoverflow.com/a/28199498/4504198 – André Betiolo Jan 28 '15 at 21:37
  • @marcoseu: OFFTOPIC: please try too look over this question related to angularAMD. : http://stackoverflow.com/questions/31288001/how-to-use-chart-js-with-angular-chart-using-requirejs – VBMali Jul 10 '15 at 07:29

3 Answers3

17

I finalized my own implementation called angularAMD and here is the sample site that uses it:

http://marcoslin.github.io/angularAMD/

It handles config functions and out of order module definitions.

Hopefully this can help other looking for something to help them with RequireJS and AngularJS integration.

marcoseu
  • 3,892
  • 2
  • 16
  • 35
  • Hi i am trying to implement angularAMD. Does using it have to follow your folder structure?? I want to include the services and directives if they are in different folders? Can i do that? – VishwaKumar Mar 03 '14 at 13:23
  • @VishwaKumar no, you do not need to follow my folder structure at all. It is all relative to `baseUrl` in your `main.js`. – marcoseu Mar 03 '14 at 14:14
  • @marcoseu: Please look over the issue(not solved yet) : https://github.com/marcoslin/angularAMD/issues/148 – VBMali Jul 10 '15 at 07:22
10

Take a look at my project in GitHub: angular-require-lazy

This project is intended to demonstrate an idea and motivate discussions. But is does what you want (check expenses-view.js, it loads ng-grid lazily).

I am very interested in comments, ideas etc.


(EDIT) The ng-grid Angular module is lazy loaded as follows:

  1. expenses-view.js is loaded lazily, when the /expenses route is activated
  2. expenses-view.js specifies ng-grid as a dependency, so RequireJs loads ng-grid first
  3. ng-grid is the one that calls angular.module(...)

In order to accomplish this, I replaced (proxied actually) the real angular.module method with my own, that supports laziness. See bootstrap.js and route-config.js (the functions initLazyModules() and callRunBlocks()).

This implementation has its drawbacks that you should be aware of:

  1. Config functions are not implemented (yet). I do not know if it is possible to lazily provide config-time dependencies.
  2. Order matters in definitions. If service A depends on B but A is defined after B in your module, DI wil fail. This is because the lazyAngular proxy executes definitions immediately, unlike real Angular that makes sure dependencies are resolved before executing the definitions.
Aurelio
  • 24,702
  • 9
  • 60
  • 63
Nikos Paraskevopoulos
  • 39,514
  • 12
  • 85
  • 90
  • Interesting implementation but it does not really answer my question on lazy loading of "angular.module", unless I am missing something. None of your expenses-view.js' dependencies are coded using "angular.module". But I will keep this in mind for what I am trying to do. – marcoseu Oct 02 '13 at 10:37
  • I copied your code in bootstrap.js, more specifically everything related to `makeLazyModule` and `lazyAngular.module` and it actually works. Now, if I only understood why it is working... – marcoseu Oct 02 '13 at 16:16
  • Took me a while but I think I finally got it. You created a proxy version of `angular.module` that calls the cached $provider during `ng-app.config` for lazy loaded modules. Brilliant!!! – marcoseu Oct 02 '13 at 19:52
  • @marcoseu Hi, thanks for the kudos; please take a look at the drawbacks of my solution, added to the answer! – Nikos Paraskevopoulos Oct 03 '13 at 14:33
  • @"Nikos Paraskevopoulos" yes, I did realize the drawback you mention and working on solving the point 2. Config is going to be tricky... – marcoseu Oct 03 '13 at 17:59
  • 2 of your sample pages, expenses-view.js and route-config.js, doesn't exist anymore. Also, whichever of your sample functions used to be in bootstrap.js (I assume `callRunBlocks()`) isn't there anymore; it looks like you have different functions now. – trysis Sep 28 '14 at 02:59
  • @trysis Yes, the project is evolving - and I am in the middle of another evolution step as well. This answer applies better to the [0.2.1](https://github.com/nikospara/angular-require-lazy/tree/v0.2.1) release. – Nikos Paraskevopoulos Sep 28 '14 at 11:26
  • I figured that was the reason, I just wanted to bring it to your attention as well as the attention of anyone else reading this answer and wondering why he/she can't find what you're talking about. – trysis Sep 28 '14 at 17:36
1

It looks like the Node.js module ocLazyLoad defines a way of doing this lazy-loading, though I'm not sure how it fares, performance-wise, compared to the methods in the other answers or hard-coding the dependencies. Any info on this would be appreciated. One interesting thing is that the other answers need RequireJS to operate, while ocLazyLoad doesn't.

It looks like ocLazyLoad defines another provider that injects the dependency after the containing module has already been instantiated. It seems to do this by essentially replicating some low-level Angular behavior, like module loading and providing, hence why it looks so complicated. It looks like it adds just about every core Angular module as a dependency: $compileProvider, $q, $injector, ng, and so many more.

trysis
  • 8,086
  • 17
  • 51
  • 80