Monday, March 10, 2014

Loading AngularJS Components With RequireJS After Application Bootstrap

Loading AngularJS Components With RequireJS After Application Bootstrap

Posted November 5, 2013 at 9:40 AM by Ben Nadel
Yesterday, I looked at loading AngularJS components after your AngularJS application has been bootstrapped. In that experiment I simply waited until DOM-Ready before I registered said components. That didn't really make them lazy-loaded, it only demonstrated that the components could be registered post-bootstrap. Today, I wanted to play around with using RequireJS in order to make the components truly lazy-loaded.

   
   
   
When I think about a cohesive set of AngularJS components, I think about two things: the JavaScript parts and the HTML parts. In order for RequireJS to lazy-load some aspect of our AngularJS application, it will have to load both the JavaScript objects and the HTML templates. In this demo, I'm using the RequireJS plugin - text.js - to load said templates.
Because RequireJS is asynchronous in nature, the code that lazy-loads the AngularJS components must also be asynchronous. This means that it must operate within some callback-based workflow. Since AngularJS already has $q - a Deferred / Promise library - I decided to encapsulate the RequireJS behavior behind a $q promise. This has the added benefit that multiple sources can listen for the resolution of the lazy-loaded components.
Simply loading and executing the "lazy" JavaScript file will register the JavaScript components. The HTML templates, on the other hand, require a little bit more work; once they have been loaded by RequireJS, the Script tags (type="text/ng-template") have to be explicitly injected into the AngularJS template cache.
That said, the following demo is yesterday's demo, refactored to use RequireJS:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
ng-app="Demo">
charset="utf-8" />
 
</span></div> <div class="line" id="file-angularjs-requirejs-htm-LC7" style="border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 255, 255); padding-bottom: 2px; padding-top: 2px;"> Loading AngularJS Components With RequireJS After Application Bootstrap</div> <div class="line" id="file-angularjs-requirejs-htm-LC8" style="border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(255, 255, 255); padding-bottom: 2px; padding-top: 2px;"> <span class="nt" style="color: navy;">
 
ng-controller="AppController">
 

Loading AngularJS Components With RequireJS After Application Bootstrap
 
 
ng-switch="subview">
 
ng-switch-when="before">
 
Before app bootstrap.
 
 
ng-switch-when="after" ng-include=" 'after.htm' ">
 
 
 
 
As you can see, the controller that wants to make use of the lazy-loaded module must use the "withLazyModule()" function. This function returns a promise; however, it also accepts callbacks which will be automatically wired into the underlying deferred resolution. When the promise has been resolved, the lazy-loaded components have been injected into the AngularJS application and are ready to be consumed.
The two files that are loaded via RequireJS are the HTML templates:
1234567891011121314
 
view rawlazy.htm hosted with ❤ by GitHub
... and the JavaScript components:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
// This component collection is intended to be all the controllers,
// services, factories, and directives (etc) that are required to
// operate the "Lazy" module.
// --
// NOTE: We are not actually creating a "module" (as in angular.module)
// since that would not work after bootstrapping.
 
 
// Lazy-loaded controller.
app.controller(
"LazyController",
function( $scope, uppercase, util ) {
 
$scope.message = util.emphasize(
uppercase( "After app bootstrap." )
);
 
}
);
 
 
// Lazy-loaded service.
app.service(
"util",
function( emphasize ) {
 
this.emphasize = emphasize;
 
}
);
 
 
// Lazy-loaded factory.
app.factory(
"emphasize",
function() {
 
return(
function( value ) {
 
return( value.replace( /\.$/, "!!!!" ) );
 
}
);
 
}
);
 
 
// Lazy-loaded value.
app.value(
"uppercase",
function( value ) {
 
return( value.toString().toUpperCase() );
 
}
);
 
 
// Lazy-loaded directive.
app.directive(
"bnItalics",
function() {
 
return(
function( $scope, element ) {
 
element.css( "font-style", "italic" );
 
}
);
 
}
);
view rawlazy.js hosted with ❤ by GitHub
As you can see, the lazy-loaded components are registered in the same way that any of your AngularJS components are registered. We didn't use a "module" here (as in angular.module()), since the new module wouldn't populate the correct dependency injection container.
There's so much more to think about here, but this is pretty exciting! I love the idea of being able to lazy-load parts of your AngularJS application. It definitely requires some more thinking and more organization; but, it could really be great for load-times.

No comments:

Post a Comment