AngularJS


with 

RequireJS


Thomas Burleson
SiriusXM Satellite Radio

Preview 

Take-away Summary



  • AngularJS injects instances
  • RequireJS injects Classes


RequireJS




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




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...

Using RequireJS with AngularJS

By Thomas Burleson

Using RequireJS with AngularJS

Why is RequireJS important to AngularJS development? Learn the how RequireJS augments your development o manage your code and packages and deliver it to AngularJS.

  • 30,134