AngularJS


with 

RequireJS


Thomas Burleson
SiriusXM Satellite Radio

Preview 

Take-away Summary



  • AngularJS injects instances
  • RequireJS injects Classes


RequireJS

http://requirejs.org/



Is it  needed with AngularJS ?

Fast-dive into  RequireJS  concepts...  

How to use RequireJS with AngularJS  ?

Is RequireJS really needed ?


                   Option #1:    Load all the code in one large Javascript file (or block)

 
    var FlightDashboard = function( $scope, user, travelService ) { /* ... */ },
        $QDecorator     = function ($provide)                     { /* ... */ };

    angular.module( "FlightServices", [ ] )
        .config(  $QDecorator                                         )
        .service( "user",           function()           { /* ... */ })
        .service( "travelService",  function( user, $q ) { /* ... */ })
        .service( "weatherService", function( $q )       { /* ... */ })


    angular.module( "FlightApp", [ "FlightServices" ] )
        .controller( "flightDashboard", FlightDashboard );



... reasonable for simple apps.

See the code dependencies !       
















... for a simple app!

Option #2:  Let's use Packages instead...
















... but we still have to manually manage the  loads  ( aka imports )

What about large apps ?


Imagine the dependency  complexities !    

for  real-world  application code ! 





We need 


Package Dependency Manager



aka Linker )

Fast-Dive into RequireJS ! 



4 Dependency Types

Great Features

3 Simple APIs


4 Types of Dependencies 




Load Dependency

Construction Dependency

Runtime Dependency

AngularJS Module Dependency




4 Great features using RequireJS  ! 



1)   Package Dependency Manager

2)    Injector   ( note: this not AngularJS Injection )

3)    Javascript File Loader

4)   Concatenate / Uglify 


RequireJS  manages :


  1. Load             dependency
  2. Runtime     dependency



Angular JS  manages :


  1. Construction          dependency ( IoC )
  2. Angular Module     dependency

3 Simple RequireJS APIs  





1)     define ( )   -  define dependencies & register factory function

2)     require ( )  -   callback function  to be invoked; when
                                                   the tree of all define()s have completed

3)     requirejs . config ( )  -  configure source pathsaliases

1.)    define ( )


Creates Asynchronous Module Definitions 
( AMD ) so files load in any order.

` Ready ` handlers called when  dependencies resolve... 
trigger immediately if no dependencies.

`Ready` handlers return values  which are stored by filename or ID... 
Values are injected into ready handlers of other AMD definitions.
 


  • Every file is wrapped in a define()
  • Every define() returns a value; cached in registry


1.) define ( )


define(

[ ], // array of dependencies `paths` to other define() modules
function onReady( ) { /** * Returns a value (Function, Constructor, String, Object, etc), */ } );

  • The anonymous function is named onReady
  • onReady is called immediately since no dependencies.
  • onReady must return an Object ( Function, Class, or instance)
  • The Object is stored in internal RequireJS registry

1.) define( ) with no dependencies :


define([ ], function ( ) {    
    var supplant =  function( template, values, pattern ) {
        pattern = pattern || /\{([^\{\}]*)\}/g;

        return template.replace(pattern, function(a, b) {
            var p = b.split('.'), r = values;

            try {
                for (var s in p) { r = r[p[s]];  }
            } catch(e){
                r = a;
            }

            return (typeof r === 'string' || typeof r === 'number') ? r : a;
        });
    };

    return String.supplant = supplant;});
  • filename                 ==   ./mindspace/utils/supplant.js 
  • Implicit  dependency ID ==   mindspace/utils/supplant

1.) define ( )



define(

    [ 'mindspace/utils/supplant' ],   

    function onReady( supplant ) 
    {
        return XXX;
    }
);           
   

  • Dependency on supplant.js file ( notice missing .js)
  • Value returned from supplant.js define() is a Function
  • Supplant() is only used within this AMD
  • This AMD define( ) returns value XXX;

1.)     define ( ) with dependencies :




    define( [ 
        'mindspace/utils/crypto/md5',
        'utils/supplant' 
    ], 
    function ( md5, supplant )
    {
        // Prepare the Gravatar Directive construction function

        var Gravatar = function( ) { /*...*/ };

        return Gravatar;

    });



  • onReady arguments are injected Values; loaded by each dependent define()
  • Can use package paths  as dependency IDs .
  • Can use use path ` alias ` notations also.

1.)    define ( )




Builds a tree of dependencies

Builds a flat registry of values stored by module ID

Values are usually references to Class or Functions
 



But NOTHING happens until require() is used!


2.)    require ( )


require( [ "main" ], function( app )
{
   // Application has bootstrapped and started...
});

  • Acts as the initialization or ROOT of the dependency tree.
  • Starts the cascade of dependency checks and script loading
  • Starts the cascade of define() triggers


3.)    requirejs.config ( )



        
require.config (
{
    appDir  : '',
    baseUrl : './src',  // relative to index.html
    paths   :
    {
        // Configure alias to full paths

        'auth'         : './quizzer/authentication',
        'quiz'         : './quizzer/quiz',
        'utils'        : './mindspace/utils'

    }
});

    

  • Configures locations to source & packages
  • Configures aliases for define ( )     

How to use RequireJS with AngularJS ?


So easy... 


  • Use define( ) around your modules...
  • Use define( ) around all your code...
  • Use requirejs.config( ) to set your paths
  • Use require( ) to launch your code to initialize AngularJS

AngularJS Controllers



define( [ 'utils/supplant' ], function ( supplant )
{
    var LoginController = function( session, authenticator, $scope, $log ) 
        { 
            $log.debug(                supplant( 'User {name}', session.user )             );
    	};

    return LoginController;
});

  • RequireJS is injecting Functions or Classes  
  • AngularJS is injecting instances of the classes

AngularJS Services

`quiz/delegates/QuizDelegate`

        
define([
        'utils/Factory',
        'quiz/builders/QuizBuilder',
        'quiz/builders/ScoreBuilder'
    ],
    function ( Factory, QuizBuilder, ScoreBuilder )
    {
        var quizBuilder  = Factory.instanceOf( QuizBuilder  ),                
            scoreBuilder = Factory.instanceOf( ScoreBuilder),
            QuizDelegate = function ( $http, $q, $log ) { /* ... */ };

        return [ "$http", "$q", "$log", QuizDelegate ];
    }
);        
    

RequireJS is injecting  Classes (definitions)
Instances  are constructed external to AngularJS  to
be used within the QuizDelegate service...

AngularJS Modules

`quiz/QuizModule`

(function ( define, angular ) {
    "use strict";

    define([
            'quiz/delegates/QuizDelegate',
            'quiz/controllers/TestController',
            'quiz/controllers/ScoreController'
        ],
        function ( QuizDelegate, TestController, ScoreController )
        {
            var moduleName = "quizzer.Quiz";

            angular.module( moduleName, [ ] )
                .service( "quizDelegate",         QuizDelegate )
                .controller( "TestController",    TestController )
                .controller( "ScoreController",   ScoreController );

            return moduleName;
        });

}( define, angular ));
       

AngularJS App

main.js

define([
        'quiz/QuizModule',
        'quiz/RouteManager',
        'auth/AuthenticateModule'
    ],
    function ( QuizModule, RouteManager, AuthenticateModule )
    {
        var appName = 'quizzer.OnlineTest',
            depends = [ "ngRoute", AuthenticateModule, QuizModule ],
            app     = angular.module( appName, depends )
                             .config( RouteManager  );

        angular.bootstrap( document.getElementsByTagName("body")[0], [ appName ]);

        return app;
    }
);

GitHub Demo: Quizzler




https://github.com/ThomasBurleson/angularjs-Quizzler

Using Grunt



Build and Deploy
Supports RequireJS using grunt plugin


  • Deploy Uncompressed
  • Deploy Minified & Obfuscated 

2 Secrets to Builds


(1)  Configure Grunt RequireJS settings 

requirejs: {
    compile: {
        options: {
            baseUrl: "../client/src",
            paths   :
            {
                // Configure alias to full paths; relative to `baseURL`

                'auth'         : './quizzer/authentication',
                'quiz'         : './quizzer/quiz',
                'utils'        : './mindspace/utils'

            },
            out: '<%= buildDir %>/assets/js/quizzler.js',
            name: 'main'

        },
        preserveLicenseComments : false,
        optimize: "uglify"
    }
}

2 Secrets to Builds


(2)   Configure Main launch point

head.js(

      { require    : "./vendor/requirejs/require.js",                  size: "80196"   },
      { angular    : "./vendor/angular/angular.js",                    size: "551057"  },
      { ngRoute    : "./vendor/angular-route/angular-route.js",        size: "30052"   },
      { quizzler   : "./assets/js/quizzler.js",                        size: "18762"   }
)
.ready("ALL", function()
{
    require( [ "main" ], function( app )
    {
        // Application has bootstrapped and started...
    });
});

  • Load the Uglified app code in js/quizzler.js 
  • Use require( ) to trigger all define( ) & initialize AngularJS

AngularJS App

require(  [ "main" ],  function( ){  } );


define([
        'quiz/QuizModule',
        'quiz/RouteManager',
        'auth/AuthenticateModule'
    ],
    function ( QuizModule, RouteManager, AuthenticateModule )
    {
        var appName = 'quizzer.OnlineTest',
            depends = [ "ngRoute", AuthenticateModule, QuizModule ],
            app     = angular.module( appName, depends )
                             .config( RouteManager  );

        angular.bootstrap( document.getElementsByTagName("body")[0], [ appName ]);

        return app;
    }
);

Demo



angularjs-Quizzler on GitHub


  • Development Build = 'grunt dev'
  • Production Build      = 'grunt prod'

About ME


Thomas Burleson


Need a senior, front-end architect ?
I am looking for the next great project...