5
votes

I am in process of outlining a architecture of a fairly complex application based on angular. So I started with the angular-seed project and it seems to be a good starting point. What bothers me is that angular apps by nature involves loading everything upfront. With script loaders, there doesn't seems to be a clean way around.

I came from a backbone.js background and there it was quiet straight to use the require.js for lazy loading based on the router callbacks. In angular routes are somewhat defined like the below:

// Declare app level module which depends on views, and components
angular.module('myApp', [
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.when({templateURL:'../tmpl.html',controller:'view1Ctrl'})
.when({templateURL:'../tmpl.html',controller:'view1Ctrl'})
.otherwise({redirectTo: '/view1'});
}]);

now here, $routeProvider.when({templateURL:'../tmpl.html',controller:'view1Ctrl'}) I would like to lazily load the controller and the template. I was tempted to use something like:

$routeProvider.when({templateURL:'../tmpl.html',controller:require('view1Ctrl'})

using browserify but then it doesn't seems to be clean and not even with require. I know this question has been asked several times on SO some way or the other but i haven't found a emphatic answer to this.

My preference here is to use the browserify as it supports the much loved cjs modules in browser.

1
As you said, due to nature of Angular.js there is "non-hacky way" of doing that. You have to expose $controllerProvider.register, $provide.factory and $compileProvider.directive at some point have to use them in run state. But, it is a hack. :( - Umut Benzer
Also, I think this is related: stackoverflow.com/questions/21742495/… - Umut Benzer

1 Answers

3
votes

I'm not sure how to do this with Browserify, as I've never tried it myself but I would strongly recommend you look into ocLazyLoad.

As a standalone service, it works wonders with loading files (json, css, js, templates - you name it) and injecting it into your already running angular application.

With that said, it works even better(imo) coupled with a router (the default angular one, or ui-router).

There are some 'seed projects' that showcase how one could do it with ocLazyLoad coupled with SystemJS.


But you don't even need that.

If you go with ui-router, ui-router-extras and ocLazyLoad you can put something like this together to lazy load states:

main.js

/** 
 * Inject the needed dependencies into our main module.
 */
var main = angular.module('main', [ 'ui.router', 'ct.ui.router.extras.future', 'oc.lazyLoad' ]);

/**
 * Define the lazy loaded states.
 */
main.constant('lazyLoadedStates', [
  {
    name: 'about',
    url:  '/about',
    type: 'lazy',
    src: [
      '/path/to/about.module.js',
      '/path/to/AboutController.js'
    ]
  }
]);

/**
 * Setup the behaviour for when we hit a futureState with the 'lazy'
 * type. 
 * 
 * 1. Setup a deferred object.
 * 2. Resolve the promise when every file defined in the futureState.src has been loaded.
 * 3. Return the promise.
 */
main.config(function ($futureStateProvider, lazyLoadedStates) {
  $futureStateProvider.stateFactory('lazy', function ($q, $ocLazyLoad, futureState) {
    var deferred = $q.defer();

    $ocLazyLoad.load(futureState.src).then(function () {
      deferred.resolve();
    });

    return deferred.promise;
  });

  lazyLoadedStates.forEach($futureStateProvider.futureState);
});

That's the 'framework' out of the way - now you just need to keep adding more modules, with more code, and match a real state definition with the dummy one in the lazyLoadedStates constant.

about.module.js

/** 
 * Setup the _real_ '/about' state in this lazy loaded file.
 */
angular.module('about', []).config(function ($stateProvider) {
  $stateProvider.state('about', {
    url: '/about',
    controller: 'AboutController',
    template: 'some_template.html'
  });
});

AboutController.js

/** 
 * Register the AboutController in a lazy loaded file. This could be done in about.module.js aswell,
 * but we'll do it here to separate stuff and showcase loading of multiple files. 
 */
angular.module('about').controller('AboutController', function ($state) {
  console.log('Im on a lazy loaded state!', $state.current);
});

I hope that gives you the rough idea of how to setup lazy loaded states in Angular. I have yet to find an easier way to do this, but I'm sure there are articles out there on how to couple ui-router (or the default angular router) with some other 'lazy-loader'.

Doc links: