Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lazy loading of components in angular with browserify

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.

like image 972
beNerd Avatar asked Oct 19 '22 13:10

beNerd


1 Answers

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.

  • https://github.com/Swimlane/angular-systemjs-seed
  • https://github.com/lookfirst/systemjs-seed
  • https://github.com/lookfirst/ocLazyLoad-SystemJS-Router
  • https://github.com/kasperlewau/ng-jspm-seed

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:

  • ocLazyLoad
  • ui-router-extras#future
like image 96
Kasper Lewau Avatar answered Nov 02 '22 08:11

Kasper Lewau