In a previous post I showed how to combine RequireJS AMD with regular Angular DI. In this post I will demonstrate how to enhance this further by adding ocLazyLoad to support true on demand lazy loading of resources.
The advantage to using ocLazyLoad is that it enables us to support lazy loading at the module level, which isn't supported in Angular by default. This greatly simplifies things if you want to retrofit lazy loading into an existing Angular application. Basically all you have to do is wire in some pluming to wrap your top level module files, but luckily we don't have to touch downstream resources like controllers, services and directives.
You can wire in the lazy loading on any event, but a natural place to start is ui-router. I have created a simple example with a navigation menu, but the key point is that javascript files pertaining to a particular section will only load when the user navigates to that section.
Here's the link to the sample app
To prove that resources are lazy loaded simply follow along in the browser's dev tools as you click on the navigation items. You should see JavaScript files get loaded as needed, but only once.
The code can be found below:
main.js
require(['application'], function (app) {
app.bootstrap();
});
application.js
define([], function () {
var app = angular.module('mainModule', ['ui.router','oc.lazyLoad']);
app.config(['$ocLazyLoadProvider','$stateProvider', '$urlRouterProvider',
function ($ocLazyLoadProvider,$stateProvider, $urlRouterProvider) {
$ocLazyLoadProvider.config({
loadedModules: ['mainModule'],
asyncLoader: require
});
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'lazy/home.html'
})
.state('module1', {
url: '/module1',
templateUrl: 'lazy/module1/module1.html',
resolve: {
load: function($ocLazyLoad) {
return $ocLazyLoad.load ({
name: 'module1',
files: ['lazy/module1/module.js']
});
}
}
})
.state('module2', {
url: '/module2',
templateUrl: 'lazy/module2/module2.html',
resolve: {
load: function($ocLazyLoad) {
return $ocLazyLoad.load ({
name: 'module2',
files: ['lazy/module2/module.js']
});
}
}
});
}]);
app.bootstrap = function () {
angular.bootstrap(document, ['mainModule']);
};
return app;
});
As you can see, the main module is pretty much like any Angular app module, with the exception of the resolve based ocLazy calls needed to bring in modules at runtime.
module1/module.js
angular.module('module1',[]);
angular.module('module1').controller('module1Controller',['service1',function(service1){
this.message = service1.getMessage();
}]);
angular.module('module1').factory('service1', function(){
return{getMessage:function(){return 'Hello from lazy loaded service';}};
});
module2/module.js
angular.module('module2',[]);
angular.module('module2').controller('module2Controller',function(){
this.message = 'Hello from a lazy loaded controller';
});
angular.module('module2').directive('greeting', function(){
return {
template:'<div>{{message}}</div>',
link:function($scope){
$scope.message = 'hello from a directive';
}};
});
There you have it! Lazy loading in Angular via RequireJS and ocLazyLoad on top of Angular DI.
Update:
I have uploaded the code to Github