Introduction
I'm studying angularjs to replace my old knockoutjs app (why ? because I want to stay hip and cool like the rest of you !), and one of the first requirements for me was that angularjs would play nice with requirejs. Turns out it's a piece of cake.
Dependency injection
First, let me tell you that both angularjs and requirejs claim to do dependency injection (DI). And they're both right. But angularjs does this at a functional level, while requirejs' realm is file loading.
In short : you need the DI of angularjs to tell it where to inject templates, use controllers and services etc. But without requirejs, you need to include all your .js files in the top-level index.html page in separate <script> tags, so the browser can load them. That's hard to maintain in any non-trivial application.
With requirejs, you need only one single <script> tag in your index.html, and requirejs does the rest ! For further information about requirejs, please refer to their website.
Here's how you do it
index.html
<!DOCTYPE html>
<html >
<head>
<meta name="viewport" content="width=device-width" />
<title>My Amazing App</title>
<link rel="stylesheet" type="text/css" href="Content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="Content/ypatm.css" />
<script type="text/javascript" data-main="scripts/app/main" src="scripts/require.js"></script>
</head>
<body>
As you can see, there are still a couple of lines for loading the css, but that's allright. Main thing is that I only need 1 <script>
tag to load ALL the .js files !
In this tag, I specify 2 things :
src="scripts/require.js"
: this is to tell the browser where to find require.js data-main="scripts/app/main"
: this is to tell requirejs where to find it's configuration and starting point (notice that the file is called "main.js", but we don't need to add the .js extension)
main.js
This is the file specified above. It contains the configuration and starting point of requirejs.
requirejs.config({
baseUrl : "scripts/app",
paths: {
jquery : '../jquery-1.9.1.min',
angular: '../angular.min'
},
shim: {
'angular': {
deps:['jquery'],
exports: 'window.angular'
}
}
});
requirejs(['angular','ypatm'], function (angular, ypatm) {
angular.bootstrap(document, ['ypatm']);
});
There are a couple of things to notice here. Let's start with the configuration part (the top half of the code above).
Angularsjs 1.x is not AMD
In the configuration you might notice 2 things. First is that apparently, angularjs seems to depend on jquery. That's not exactly true, but since I use bootstrap in my project, and bootstrap *is* dependent on jquery, I reacon it would be a good idea to let them depend on the same version.
Secondly, angularjs is not conform to AMD (Asynchrous Module Definition), meaning it is not wired to work with requirejs. Luckily requirejs has a solution for that using a "shim" configuration as you can see above. (I think angularjs 2.x *does* conform to AMD, but I'm not sure. In that case, you would only need the "paths" bit, and leave out the "shim" part).
Simple, isn't it ?
Now let's have a look at the startup function, "requirejs".
In this function, we "instantiate" the 2 top js-files we need for our application : angular and my own application, which has the anagram "ypatm" (don't bother to find out what it means !).
Use angular.bootstrap, not the attribute "ng-app"
That was the hardest part for me to figure out. Every angularjs application has the directive "ng-app
" as an attribute to it's very first <html>
tag ! Not so when you use requirejs. The reason is simple : the directive "ng-app" has no meaning before angularjs is loaded, and angularjs is loaded only after requirejs is loaded.
So, saying to the browser : "use this directive" of a library that is not even loaded makes little sense. Fortunately (and I guess there is a bit of luck involved), angularjs offers an alternative to the "ng-app" directive, and this is to call the "bootstrap" function. That ties angularjs to the document.
And that's it ! Nothing else to do than to write that killer app of yours !
But just to see how the DI of angularjs and requirejs differ, let's have a look at my main app file, so eloquently called "yatpm.js".
ypatm.js
define(['angular', 'configuration', 'deploy', 'message'], function(angular, configuration, deploy, message) {
var app = angular.module('ypatm', ['message', 'configuration', 'deploy']);
app.filter("sanitize", ['$sce', function ($sce) {
return function (htmlCode) {
return $sce.trustAsHtml(htmlCode);
}
}]);
app.controller("PanelController", function() {
var tab = 1;
return {
selectTab: function(setTab) {
tab = setTab;
},
isSelected: function (checkTab) {
return tab === checkTab;
}
};
});
});
The first line is the requirejs injection. It tells requirejs what files it need to "instantiate" (when it hasn't done so already) : "angular.js", "configuration.js", "deploy.js" and "message.js".
The second line is the angularjs injection. It tells angularjs there's a new module called "ypatm", that depends on the angularjs modules "message", "configuration" and "deploy". If the requirejs DI wouldn't be active, angularjs would not find the files in the first place, let alone the controller that they contain. (or you had to put them all in <script>
tags in the index.html, which was kind of the whole point to avoid !)
So, angularjs and requirejs play very nicely together, don't you think ?
History
version 0.1.0