AngularJS From Beginning: Lazy Loading Of Angular Files - Part 16

In this article, we will discuss how to perform on demand loading of AngularJS files using requirejs. This is part 16 of the article series.

I am here to continue the discussion around AngularJS. Today, we will discuss how to perform lazy loading or on demand loading of angular js related files using RequireJS. Also in case you have not had a look at our previous articles of this series, go through the following links:

In this article, we will discuss how use angularjs with require js.

Organizing modules with RequireJS and AMD

RequireJS is a popular script loader written by James Burke - a developer who has been quite instrumental in helping shape the AMD module format, which we’ll discuss more shortly. Some of RequireJS’s capabilities include helping to load multiple script files, helping define modules with or without dependencies and loading in non-script dependencies such as text files.

RequireJS is compatible with the AMD (Asynchronous Module Definition) format, a format which was born from a desire to write something better than the “write lots of script tags with implicit dependencies and manage them manually” approach to development. In addition to allowing you to clearly declare dependencies, AMD works well in the browser, supports string IDs for dependencies, declaring multiple modules in the same file and gives you easy-to-use tools to avoid polluting the global namespace. Think about the GMail web-client for a moment. When users initially load up the page on their first visit, Google can simply hide widgets such as the chat module until a user has indicated (by clicking “expand”) that they wish to use it. Through dynamic dependency loading, Google could load up the chat module only then, rather than forcing all users to load it when the page first initializes. This can improve performance and load times and can definitely prove useful when building larger applications.

Writing AMD modules with RequireJS

As discussed above, the overall goal for the AMD format is to provide a solution for modular JavaScript that developers can use today. The two key concepts you need to be aware of when using it with a script-loader are a define() method for facilitating module definition and a require() method for handling dependency loading. As you can tell by the inline comments, the module_id is an optional argument which is typically only required when non-AMD concatenation tools are being used (there may be some other edge cases where it’s useful too). When this argument is left out, we call the module “anonymous”. When working with anonymous modules, the idea of a module’s identity is DRY, making it trivial to avoid duplication of filenames and code. Back to the define signature, the dependencies argument represents an array of dependencies which are required by the module you are defining and the third argument (“definition function”) is a function that’s executed to instantiate your module. A barebone module (compatible with RequireJS) could be defined using define() as follows:

  1. define(['foo''bar'],  
  2.     // module definition function    
  3.     // dependencies (foo and bar) are mapped to function parameters    
  4.     function(foo, bar)   
  5.        {  
  6.         // return a value that defines the module export    
  7.         // (i.e the functionality we want to expose for consumption)    
  8.         // create your module here    
  9.         var myModule =   
  10.             {  
  11.             doStuff: function()   
  12.               {  
  13.                 console.log('Yay! Stuff');  
  14.             }  
  15.         }  
  16.         return myModule;  
  17.     });    

Alternate syntax

There is also a sugared version of define() available that allows you to declare your dependencies as local variables using require(). This will feel familiar to anyone who’s used node, and can be easier to add or remove dependencies. Here is the previous snippet using the alternate syntax:

  1. define(function(require)  
  2. {  
  3.     // module definition function    
  4.     // dependencies (foo and bar) are defined as local vars    
  5.     var foo = require('foo'),  
  6.         bar = require('bar');  
  7.     // return a value that defines the module export    
  8.     // (i.e the functionality we want to expose for consumption)    
  9.     // create your module here    
  10.     var myModule =   
  11.     {  
  12.         doStuff: function()   
  13.         {  
  14.             console.log('Yay! Stuff');  
  15.         }  
  16.     }  
  17.     return myModule;  
  18. });    

The require() method is typically used to load code in a top-level JavaScript file or within a module should you wish to dynamically fetch dependencies. An example of its usage is:

  1. // Consider 'foo' and 'bar' are two external modules    
  2. // In this example, the 'exports' from the two modules loaded are passed as    
  3. // function arguments to the callback (foo and bar)    
  4. // so that they can similarly be accessed    
  5. require(['foo''bar'], function(foo, bar)   
  6. {  
  7.     // rest of your code here    
  8.     foo.doSomething();  
  9. });   
For demonstrating the above concept, I simple create a simple angular js program as below which demonstrates some data in a table format.
 
Index.html
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <title>AngularJS Filter</title>  
  5.     <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />  
  6.     <script src="require.js" data-main="app/main.js"></script>  
  7. </head>  
  8. <body data-ng-controller="FilterController" data-ng-cloak>  
  9.     <div class="panel panel-default">  
  10.         <div class="panel-heading">  
  11.             <h3>  
  12.                 Products  
  13.                 <span class="label label-primary">{{products.length}}</span>  
  14.             </h3>  
  15.         </div>  
  16.         <div class="panel-body">  
  17.             <table class="table table-striped table-bordered table-condensed">  
  18.                 <thead>  
  19.                     <tr>  
  20.                         <td>Name</td>  
  21.                         <td>Category</td>  
  22.                         <td>Offer Date</td>  
  23.                         <td class="text-right">Quantity</td>  
  24.                         <td class="text-right">Price</td>  
  25.                     </tr>  
  26.                 </thead>  
  27.                 <tbody>  
  28.                     <tr data-ng-repeat="p in products | orderBy :'name'">  
  29.                         <td>{{p.name | uppercase}}</td>  
  30.                         <td>{{p.category | lowercase}}</td>  
  31.                         <td>{{getExpiryDate(p.expiry) | date:"dd MMM yy"}}</td>  
  32.                         <td class="text-right">{{p.quantity | number:2 }}</td>  
  33.                         <td class="text-right">{{p.price | currency}}</td>  
  34.                     </tr>  
  35.                 </tbody>  
  36.             </table>  
  37.         </div>  
  38.     </div>  
  39. </body>  
  40. </html>  
Main.js
  1. require.config({  
  2.     urlArgs: "bust=" + (new Date()).getTime(),  
  3.     paths: {  
  4.         'angular': '../../ch20(RequireJs)/angular',  
  5.   
  6.         'FilterController': 'Index'  
  7.     },  
  8.     shim: {  
  9.         'angular': {  
  10.             exports: 'angular'  
  11.         },  
  12.         'app': {  
  13.             deps:  
  14.                 ['angular']  
  15.         },  
  16.         'FilterController': {  
  17.             deps: ['app']  
  18.         },  
  19.     },  
  20.     deps: ['app']  
  21. });  
  22.   
  23. require(['FilterController'], function () {  
  24.     angular.bootstrap(document, ['TestApp']);  
  25. });  
App.js
  1. define(['require'], function (require) {      
  2.     var testApp = angular.module('TestApp', []);  
  3.   
  4.     testApp.run(['$rootScope', function ($rootScope) {  
  5.   
  6.     }]);  
  7. });  
Index.js (Controller)
  1. define(['angular'], function (angular) {  
  2.     var testApp = angular.module('TestApp');  
  3.       
  4.     testApp.controller('FilterController', ['$scope', function ($scope) {      
  5.         $scope.products = [  
  6.                             { name: "Sony LED", category: "TV", price: 40000, quantity: 10, expiry: 30 },  
  7.                             { name: "Samsung", category: "TV", price: 35640, quantity: 08, expiry: 21 },  
  8.                             { name: "Z30", category: "Mobile", price: 36000, quantity: 5, expiry: 50 },  
  9.                             { name: "Iphone 6", category: "Mobile", price: 55000, quantity: 6, expiry: 60 },  
  10.                             { name: "Galaxy Note 3", category: "Mobile", price: 45000, quantity: 15, expiry: 50 },  
  11.         ];  
  12.   
  13.         $scope.getExpiryDate = function (days) {  
  14.             var now = new Date();  
  15.             return now.setDate(now.getDate() + days);  
  16.         }  
  17.     }]);  
  18. });