From a feature request I have for inclusion of an AMD loader as part of the additional ng-modules.
Angular comes with a feature that allows users to create Module definitions and look up entities such as controllers later by a String Identifier. Angular does not come with the ability to load scripts stored in a separate file which means that you are left with four options:
- Store all your javascript in a single file.
- Separate your javascript into separate files and add script tags for each file.
- Use an AMD file loader like requireJS to manage script files and their dependencies.
- Use a precompiler like browserify to merge nodejs style files into a single script file.
As a project grows, the files become more difficult to manage, not just because of their size, but because the dependencies between modules become more complicated. Large projects can also benefit from the lazy loading of an AMD loader.
An AMD module loader lists the dependencies that need to be present before this file can be run. The problem is that AMD dependencies are close to the Injection list used by Angular but not exactly the same thing. One problem can be seen in this code:
define(['angular'], function(angular) {
return angular.module('myApp.controllers', ['myApp.services'])
.controller('MyController', ['$scope', 'myService',
function($scope, myService) {
$scope.data = myService.getData();
}
])
};
The define statement at the top says to make sure that angular
is initialized before running this function. The angular.module
statement says to lookup '$scope' and 'myService' and inject them into the variables $scope
and myService
. The problem is that the AMD loader might not have initialized the file that defines myApp.services
, in which you can presume is defined myService
, because it did not appear in the define statement above. This creates a huge disconnect between the injection list and AMD dependency list. Not only is it difficult to tell if 'myApp.services; has been loaded, it is also difficult to tell what particular components are available in 'myApp.services'. IOW Is 'myService' both loaded and injectable?
I currently use option #3 in the form of requirejs because I need to have the ability to dynamically load controllers via routing (See the first link). Also, the application's size I am working with now makes browserfy impractical since there a so many pages. I do, however, agree that this is sub-optimal but I don't see any other choice at the moment.
From a practical standpoint, I define one injectable per file. I also try to make all injectable arrays match the require array. This isn't necessary but it makes the code more maintainable.
I found these articles helpful in writing this:
https://github.com/marcoslin/angularAMD
http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx