Inject module dynamically, only if required

gustavohenke picture gustavohenke · Sep 3, 2013 · Viewed 56.3k times · Source

I'm using Require.js in combination with Angular.js.

Some controllers need huge external dependencies which others don't need, for example, FirstController requires Angular UI Codemirror. That's a extra 135 kb, at least:

require([
  "angular",
  "angular.ui.codemirror" // requires codemirror itself
], function(angular) {
  angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});

I don't want to have to include the directive and the Codemirror lib everytime my page loads just to make Angular happy.
That's why I'm right now loading the controller only when the route is hit, like what's done here.

However, when I need something like

define([
  "app",
  "angular.ui.codemirror"
], function(app) {
  // ui-codemirror directive MUST be available to the view of this controller as of now
  app.lazy.controller("FirstController", [
    "$scope",
    function($scope) {
      // ...
    }
  ]);
});

How can I tell Angular to inject ui.codemirror module (or any other module) in the app module aswell?
I don't care if it's a hackish way to accomplish this, unless it involves modifying the code of external dependencies.

If it's useful: I'm running Angular 1.2.0.

Answer

Nikos Paraskevopoulos picture Nikos Paraskevopoulos · Sep 12, 2013

I have been trying to mix requirejs+Angular for some time now. I published a little project in Github (angular-require-lazy) with my effort so far, since the scope is too large for inline code or fiddles. The project demonstrates the following points:

  • AngularJS modules are lazy loaded.
  • Directives can be lazy loaded too.
  • There is a "module" discovery and metadata mechanism (see my other pet project: require-lazy)
  • The application is split into bundles automatically (i.e. building with r.js works)

How is it done:

  • The providers (e.g. $controllerProvider, $compileProvider) are captured from a config function (technique I first saw in angularjs-requirejs-lazy-controllers).
  • After bootstraping, angular is replaced by our own wrapper that can handle lazy loaded modules.
  • The injector is captured and provided as a promise.
  • AMD modules can be converted to Angular modules.

This implementation satisfies your needs: it can lazy-load Angular modules (at least the ng-grid I am using), is definitely hackish :) and does not modify external libraries.

Comments/opinions are more than welcome.


(EDIT) The differentiation of this solution from others is that it does not do dynamic require() calls, thus can be built with r.js (and my require-lazy project). Other than that the ideas are more or less convergent across the various solutions.

Good luck to all!