Monday, May 13, 2013

Making RequireJS and AngularJS Play Together

If you have to make AngularJS and RequireJS play together, here's how to do it.

1. Make Angular wait until you're ready.

  • Remove the ng-app from your HTML. This way, Angular won't run before Require has gathered dependencies.
  • In your main JavaScript file for Require, start Angular manually:
require([], function(){
  
  //... snip ...

  //start angular after dependencies are gathered
  //I'm using document here, but it should be whatever element 
  //you'd put the ng-app on normally
  angular.bootstrap(document, ["moduleWithControllers"]); 
});

2. Put your RequireJS stuff into an Angular module

  • This lets you stay Angular-y from inside Angular code!
  • This should also be in the main .js file for your Require stuff, before running bootstrap()
require(['someDependency'], function(someDependency){
  
  angular.module('requireStuff')
    .factory('someDependency', function(){
      //make this require object or whatever available to angular
      return someDependency;
    });

  //from section #1
  angular.bootstrap(document, ["moduleWithControllers"]); 
});

Then from the Angular code:
angular.module('moduleWithControllers', ['requireStuff'])
  .controller('MyCtrl', function($scope, someDependency){
    //do something with someDependency
  });

3. Get Angular libraries into your Require modules (Optional)

  • This lets you access libraries provided by Angular from outside of Angular code.
  • Warning: These won't be available until after Angular runs the first time, after all Require dependencies are gathered
define([], function(){
  var result = {
    $http: null,
    $q: null
  };

  angular.module('requireLibStuff')
    .service('randomService', function($q, $http){
      result.$http = $http;
      result.$q = $q;
    });

  return result;
});
Then you just need to reference the service from your angular code:
angular.module('main', ['requireLibStuff'])
  .controller('MyCtrl', function(randomService){
    //randomService won't be used, it's only referenced here 
    //to make Angular run the code above
  });

Update

I forgot to mention something for step #3 above: most Angular-provided libraries only work if called within Angular code. For example, the $q.defer() object will only notify its listeners if resolve/reject are called from within the regular Angular $apply and the like.