I am confused about the proper way to implement requireJS (and the r.js optimizer) into a very large AngularJS SPA. I would like some advice on how to organize the project so I get the benefit of AMD modularization, but not over complicate things (wrapping angular modules inside require js defined modules, which are all dumped into an optimized .js file...).
I have a few different categories of files I'm dealing with:
I have a bundle of vendor libraries (jquery, angular, angular-resource, require, lodash, etc)
I have some container level AMD modules (authentication, analytics, etc)
I have lots of angular modules, services, filters, etc
Using r.js to concat and minify all the vendor stuff together seems like a no-brainer, but when I started adding in all the other code, I felt like my project got muddy. For example, I have an Auth service, a couple controllers, and my vendor dependencies (jquery...). So my main.js that require looks at is sorta like this:
require.config({
paths: {
// load vendor libraries
'domReady': '../vendor/requirejs-domready/domReady',
'angular': '../vendor/angular/angular',
'jquery': '../vendor/jquery/jquery.min',
requireLib: '../vendor/requirejs/require'
... lots of these
//load app code
'app': '../app/app',
'AuthCtrl': '../app/modules/auth/AuthCtrl',
'UserCtrl': '../app/modules/auth/UserCtrl',
'SettingsCtrl': '../app/modules/auth/SettingsCtrl',
// potentially dozens and dozens of these...
//load auth library
'auth': '../app/modules/common/auth',
'analytics' '../analytics'
},
include: ['domReady', 'requireLib'],
shim: {
'angular': {
exports: 'angular'
}
}
...
});
Currently, I have been dumping all of the code from all 3 'categories' above into 1 big uglified js file.
What I'm not happy with is I feel like my controller is starting to need a laundry list of module dependencies:
require('app',[
'angular',
'jquery',
'AuthCtrl',
'UserCtrl',
'SettingsCtrl',
' ... potentially dozens? '
...
], function (angular, $, Auth, user, settings, ...potentially dozens?) {
'use strict';
return angular.module('ngAD', ['AuthCtrl', 'UserCtrl', 'SettingsCtrl' ...]);
});
So my question is two fold I guess:
What is the benefit of keeping all my angular modules, controllers, filters, etc, defined in their own AMD modules and included manually via requirejs? They are all in memory already (having been loaded into the big pile-o-files via r.js). Is there a smarter way to handle all these discreet angular modules when there will be tons of them? Should I concat them all into their own module that can be a single injectable dependency?
I'm getting mixed up between my "AMD modules" and "angular modules". Do all my angular components (controllers, directives, etc) need to be in their own AMD modules? I feel like I'm wrapping a module (angular) inside a module (require's AMD format).
There should be no need to do all of that.
I used this as a starting point
https://github.com/Matthew-Odette/angular-require-bootstrap-seed
Then all that is required is to add controllers and services to the require section at the end of the AMD file. e.g.
require(
[
// Dependencies from lib
'angular',
'ngRoute',
'../lib/less.min.1.5.0',
// Angular directives/controllers/services
'app',
'core/viewHomeController',
'core/commonRoutes',
'core/header',
'events/events-ctrl
],
function (angular) {
var AppRoot = angular.element(document.getElementById('ng-app'));
AppRoot.attr('ng-controller','AppCtrl');
angular.bootstrap(document, ['TheApp']);
});
events-ctrl.js being new controller/service that's been added, further controllers/services would be added the same way
Then the controllers/services needs to be wrapped in require code e.g. this is beginning of events-ctrl.js
define(['app'], function (app) {
app.factory('EventService', function($resource) {
return $resource('/api/events/:id');
});
app.controller('EventListCtrl', ['$scope', '$modal', 'EventService', function($scope, $modal, EventService) {
console.log('EventListCtrl working');
...
Related
I'm developing simple AngularJS (I'm quite new with Angular) application and I encountered problem that js are loading in wrong order. Below there are relevant (I hope) snippets of my app.
index.html
(...)
<script data-main="scripts/main.js" src="scripts/require.js"></script>
(...)
main.js
requirejs.config({
baseUrl: 'scripts',
paths: {
angular: '../../bower_components/angular/angular',
jquery: 'jquery',
domReady: 'domReady',
'jquery.scrollTo': '../../bower_components/jquery.scrollTo/jquery.scrollTo',
},
shim: {
jquery: {deps: ['domReady']},
'jquery.scrollTo': {deps: ['jquery']},
angular: {deps: ['jquery.scrollTo'], exports: 'angular'}
},
packages: []
});
requirejs(['angular', 'domReady', 'jquery', 'app', 'scripts', 'jquery.scrollTo'], function(angular, domReady) {
'use strict';
domReady(function() {
angular.bootstrap(document, ['myApp']);
});
});
app.js
define('app', ['angular', 'domReady', 'jquery', 'jquery.scrollTo'], function (angular) {
return angular.module('myApp', []);
});
scripts.js
require(['domReady', 'jquery', 'jquery.scrollTo'], function(domReady, $) {
domReady(function() {
console.log('scripts.run');
});
});
I assumed that loading order should be following:
main
domReady
jquery
jquery.scrollTo
angular
scripts
app
But in real loading order is following:
The most strange for me is why scripts.js is loaded before jquery.js and jquery.scrollTo.js if require statement define that it is dependent on domReady, jquery and jquery.scrollTo?
I suggest to remove jquery completely from your code (if its possible for you) and requirejs. AngularJS has great dependency injection module which you could use to support timing of loaded parts.
Also - you could try to inline ng-app attribute inside html code insted of invoking angular via boostrap call, than you won't need "domReady".
If you can, please tell something more about your app so we can give you better response on availible solutions.
deitch's statement regarding loading order is correct. RequireJS will guarantee that the order in which the factory functions of your modules (factory == the callback you give to define) are executed will respect the dependencies specified. As far as the order in which modules are fetched the only thing you can be sure is that a module will be fetched before its factory function is executed but RequireJS otherwise has the freedom to fetch modules in any order.
In the case you are showing us, note how the modules that are first fetched after main are those which do not have a shim configuration defined for them. Modules with a shim configuration require a bit more processing from RequireJS and thus follow a different code path.
While we're at it, a few bizarre things in your code:
A path configuration that just repeats the name of the module does nothing. For instance, jquery: 'jquery'. You could remove such path specifications.
You have require at the top level of scripts.js. You should have define there instead.
Your call to define in app.js specifies a module name (1st parameter to define). Leave the module name out.
You give a jQuery a shim configuration. jQuery calls define. Giving a shim to a module that calls define is likely to result in undefined behavior. Don't do this.
In all the tutorials and examples I've read regarding Angularjs, they all define modules with an empty list as the second parameter:
angular.module('myModule', []);
I understand that the presence of the second parameter is required to create a new module, but I don't understand why there are never any elements in the list. The document for angular.module says nothing about what the contents of the list would represent.
However, from defining my app module I understand the list represents modules upon which the new module depends - so why is it always empty for app sub-modules? For example, in my own project I've got a users module upon which my app depends:
/* app.js */
angular.module('myApp', [
'ngCookies',
'ngResource',
'ui.bootstrap',
'ui.router',
'myApp.system',
'myApp.users'
]);
angular.module('myApp.system', []);
angular.module('myApp.users', []);
When I finally got around to learning how to unit test using karma and jasmine, I spent hours trying to figure out this error message:
Error: [$injector:modulerr] Failed to instantiate module myApp.users due to:
Error: [$injector:unpr] Unknown provider: $stateProvider
http://errors.angularjs.org/1.2.13/$injector/unpr?p0=%24stateProvider
at /Users/matt/Development/myApp/public/lib/angular/angular.js:3556
at getService (/Users/matt/Development/myApp/public/lib/angular/angular.js:3683)
at invoke (/Users/matt/Development/myApp/public/lib/angular/angular.js:3710)
at /Users/matt/myApp/public/lib/angular/angular.js:3639
Eventually I found two things that would fix this problem - either I could load module dependencies in the test code, or I could add the dependencies to the empty list in the users module declaration:
/* UserControllerTest.js */
describe('UserCtrl', function () {
var $rootScope,
$scope,
controller;
beforeEach(function () {
module('ui.router');
module('myApp.system');
module('ngResource');
module('myApp.users');
inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
controller = $injector.get('$controller')('UserCtrl', {$scope: $scope});
});
});
it('should work', function () {
expect(true).toBe(true);
});
});
Or:
/* app.js */
...
angular.module('myApp.users', [
'ngResource',
'ui.router',
'mean.system'
]);
Is there some reason I wouldn't want to do the latter? Why is it that I never see that in docs and tutorials - would it prevent me from being able to mock those dependencies in testing?
Why is it that I don't need the latter sub-module definition for regular operation of my app? I do have a series of 'injection locals' specified for UserCtrl - why isn't that sufficient for the unit test?
The purpose of modules is to encapsulate self-contained parts of code (e.g. a reusable widget, code tgst implements a specific feature etc). In general, it is a good practice to have each module declare the dependencies it relies upon.
If not, then the module relies on the modules thst require it to declare those dependencies, which messes "self-containment", hurts testability and reusability and introduces a bunch if potential future bugs.
That said, there doesn't seem to be a reason not to declare dependencies with each module. (And no, it will not prevent you from mocking the dependencies in unit tests.) Of cource, as one would expect, each module is loaded once even if it is required by several modules.
The API reference is indeed not very detailed about angular.module, but the Developer Guide has a more extensive description.
E.g., quoting the "Dependencies" section:
Modules can list other modules as their dependencies. Depending on a module implies that required module needs to be loaded before the requiring module is loaded. In other words the configuration blocks of the required modules execute before the configuration blocks of the requiring module. The same is true for the run blocks. Each module can only be loaded once, even if multiple other modules require it.
ZI'm building an app where based on user preferences/configuration, I'll be loading different modules into the page.
To accomplish this, when the page loads, I run a script which builds a list of script-tags and adds all those tags to the body of the page. I also build a dependency injector list, and include the dependencies when the app is loaded.
I'm delaying app load using by putting
window.name = "NG_DEFER_BOOTSTRAP!";
and then after the script tags are added, I run
angular.element(document).ready(function () {
setTimeout(function(){
angular.resumeBootstrap();
},1000);
});
I have two problems.
First of all, for some reason, if I include angular.js in the configuration file, I get errors referring to angular not being found.
I can see that angular is being downloaded to the browser, but I'm guessing it's a timing issue, as angular takes longer to download than many of the other files.
I was going to just put angular in a regular script tag, but then I found that one of the modules I'm loading requires jQuery and jQuery UI, both to be loaded before angular, so that means more files loaded as scripts, and possibly not needed for that user.
I looked at using require.js and passing the config params to
requirejs.config.paths = //the custom built list from the users configuration file
But, from what I understand, that would also mean I'd need to wrap all of my modules in
requirejs(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// my module stuff here
});
Any other suggestions on how to accomplish this? Is there a better AMD than require.js?
I'd post all my code here, but I don't think it would help as the problem is coming from the browser downloading the files after the document is ready.
RequireJs is the best AMD implementation as far as I know.
If you want to specify your modules, you'd better to wrap your moduls in define calls:
define(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// DO NOT FORGET TO RETURN YOUR MODULE!
});
Also, why don't you start your applicaiton on domReady? You may use this requirejs plugin.
Here is a basic requirejs configuration for your case:
requirejs.config({
paths : {
jquery : 'libs/jquery',
jqueryUi : 'libs/jqueryui',
angular : 'libs/angular'
},
shim : {
angular : {exports : 'angular', deps : ['jquery']},
jqueryUI: {exports : '$', deps : ['jquery']}
},
map : {
'*' : {
domReady : 'require-plugins/domReady'
}
}
});
requirejs(['domReady!', 'angular'], function(document, angular) {
//Thank to domReady!, this callback executes on domReady events. And no need in jquery or angular:)
});
I have a bunch of Angular modules declared in my app. I originally started declaring them using the "chained" syntax like this:
angular.module('mymodule', [])
.controller('myctrl', ['dep1', function(dep1){ ... }])
.service('myservice', ['dep2', function(dep2){ ... }])
... // more here
But I decided that wasn't very easy to read, so I started declaring them using a module variable like this:
var mod = angular.module('mymodule', []);
mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
mod.service('myservice', ['dep2', function(dep2){ ... }]);
...
The second syntax seems a lot more readable to me, but my only complaint is that this syntax leaves the mod variable out in the global scope. If I ever have some other variable named mod, it would be overridden with this next one (and other issues associated with global variables).
So my question is, is this the best way? Or would it be better to do something like this?:
(function(){
var mod = angular.module('mymod', []);
mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
mod.service('myservice', ['dep2', function(dep2){ ... }]);
...
})();
Or does it even matter enough to care? Just curious to know what the "best practices" are for module declaration.
'Best' way to declare a module
As angular is on the global scope itself and modules are saved to its variable you can access modules via angular.module('mymod'):
// one file
// NOTE: the immediately invoked function expression
// is used to exemplify different files and is not required
(function(){
// declaring the module in one file / anonymous function
// (only pass a second parameter THIS ONE TIME as a redecleration creates bugs
// which are very hard to dedect)
angular.module('mymod', []);
})();
// another file and/or another anonymous function
(function(){
// using the function form of use-strict...
"use strict";
// accessing the module in another.
// this can be done by calling angular.module without the []-brackets
angular.module('mymod')
.controller('myctrl', ['dep1', function(dep1){
//..
}])
// appending another service/controller/filter etc to the same module-call inside the same file
.service('myservice', ['dep2', function(dep2){
//...
}]);
// you can of course use angular.module('mymod') here as well
angular.module('mymod').controller('anothermyctrl', ['dep1', function(dep1){
//..
}])
})();
No other global variables are required.
Of course it depends all on preferences, but I think this is kind of the best practise, as
you don't have to pollute the global scope
you can access your modules everywhere and sort them and their functions into different files at will
you can use the function-form of "use strict";
the loading order of files does not matter as much
Options for sorting your modules and files
This way of declaring and accessing modules makes you very flexible. You can sort modules via function-type (like described in another answer) or via route, e.g.:
/******** sorting by route **********/
angular.module('home')...
angular.module('another-route')...
angular.module('shared')...
How you sort it in the end is a matter of personal taste and the scale and type of the project. I personally like to group all files of a module inside of the same folder (ordered into sub-folders of directives, controllers, services and filters), including all different test-files, as it makes your modules more reusable. Thus in middle-sized projects I end up with a base-module, which includes all basic routes and their controllers, services, directives and more or less complex sub-modules, when I think they could be useful for other projects as well,e.g.:
/******** modularizing feature-sets **********/
/controllers
/directives
/filters
/services
/my-map-sub-module
/my-map-sub-module/controllers
/my-map-sub-module/services
app.js
...
angular.module('app', [
'app.directives',
'app.filters',
'app.controllers',
'app.services',
'myMapSubModule'
]);
angular.module('myMapSubModule',[
'myMapSubModule.controllers',
'myMapSubModule.services',
// only if they are specific to the module
'myMapSubModule.directives',
'myMapSubModule.filters'
]);
For very big projects, I sometimes end up grouping modules by routes, as described above or by some selected main routes or a even a combination of routes and some selected components, but it really depends.
EDIT:
Just because it is related and I ran into that very recently again: Take good care that you create a module only once (by adding a second parameter to the angular.module-function). This will mess up your application and can be very hard to detect.
2015 EDIT on sorting modules:
One and a half year of angular-experience later, I can add that the benefits from using differently named modules within your app are somewhat limited as AMD still does not really work well with Angular and services, directives and filters are globally available inside the angular context anyway (as exemplified here). There is still a semantic and structural benefit though and it might be helpful being able to include/ exclude a module with a single line of code commented in or out.
It also almost never makes much sense to separate sub-modules by type (eg. 'myMapSubModule.controllers') as they usually depend on each other.
I love the angular-styleguide by Johnpapa, and here are some rules that related to this question:
Rule: Named vs Anonymous Functions
Avoid using anonymous functions:
// dashboard.js
angular
.module('app')
.controller('Dashboard', function() { })
Instead, use named functions:
// dashboard.js
angular
.module('app')
.controller('Dashboard', Dashboard);
function Dashboard() { }
As the author says: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.
Rule : Define 1 component per file.
Avoid multiple components in one file:
angular
.module('app', ['ngRoute'])
.controller('SomeController', SomeController)
.factory('someFactory', someFactory);
function SomeController() { }
function someFactory() { }
Intead, use one file to define the module:
// app.module.js
angular
.module('app', ['ngRoute']);
one file just uses the module to define a component
// someController.js
angular
.module('app')
.controller('SomeController', SomeController);
function SomeController() { }
and another file to define another component
// someFactory.js
angular
.module('app')
.factory('someFactory', someFactory);
function someFactory() { }
Of course, there are many other rules for modules, controllers and services that are quite useful and worth reading.
And thanks to comment of ya_dimon, the above code should be wrapped in IIFE, for example:
(function (window, angular) {
angular.module('app')
.controller('Dashboard', function () { });
})(window, window.angular);
I recently had this conundrum as well. I had started off just like you using the chained syntax, but in the long run it becomes unwieldy with large projects. Normally I'd create a controllers module, a services module and so on in separate files and inject them into my main application module found in another file. For Example:
// My Controllers File
angular.module('my-controllers',[])
.controller('oneCtrl',[...])
.controller('twoCtrl',[...]);
// My Services File
angular.module('my-services',[])
.factory('oneSrc',[...])
.facotry('twoSrc',[...]);
// My Directives File
angular.module('my-directives',[])
.directive('oneDrct',[...])
.directive('twoDrct',[...]);
// My Main Application File
angular.module('my-app',['my-controllers','my-services','my-directives',...]);
But each one of these files was getting way to large as the project grew. So I decided to break them up into separate files based on each controller or service. I found that using angular.module('mod-name'). without the injection array, is what you need for this to work. Declaring a global variable in one file and expecting that to be readily available in another just doesn't work or could have unexpected results.
So in short my application looked something like this:
// Main Controller File
angular.module('my-controllers',[]);
// Controller One File
angular.module('my-controllers').controller('oneCtrl',[...]);
//Controller Two File
angular.module('my-controllers').controller('twoCtrl',[...]);
I did this to the services file as well, no need to change the main application module file you'd still be injecting the same modules into that.
One other practice is to stuff controllers, directives, etc in their own modules and inject those modules into your "main" one:
angular.module('app.controllers', [])
.controller('controller1', ['$scope', function (scope) {
scope.name = "USER!";
}]);
angular.module('app.directives', [])
.directive('myDirective', [function () {
return {
restrict: 'A',
template: '<div>my directive!</div>'
}
}]);
angular.module('app', [
'app.controllers',
'app.directives'
]);
Nothing is left in the global scope.
http://plnkr.co/edit/EtzzPRyxWT1MkhK7KcLo?p=preview
I like to divide my files and my modules.
Something like this:
app.js
var myApp = angular.module('myApp', ['myApp.controllers', 'myApp.directives', 'myApp.services']);
myApp.config(['$routeProvider', function($routeProvider) {
/* routes configs */
$routeProvider.when(/*...*/);
}]);
directives.js
var myDirectives = angular.module('myApp.directives', []);
myDirectives.directive( /* ... */ );
service.js
var myServices = angular.module('myApp.services', []);
myServices.factory( /* ... */ );
Im not a big fan of the "chained style", so I prefer to write down my variable always.
I suggest to follow Angularjs Style Guide.
They handle all concept from naming convention, to modularize your app and so on.
For angular 2, you can check Angular 2 Style Guide
For me, chaining is the most compact way:
angular.module("mod1",["mod1.submod1"])
.value("myValues", {
...
})
.factory("myFactory", function(myValues){
...
})
.controller("MainCtrl", function($scope){
// when using "Ctrl as" syntax
var MC = this;
MC.data = ...;
})
;
That way I can easily move components between modules, never need to declare the same module twice, never need any global variables.
And if the file gets too long, solution is simple - split into two files, each declaring its own module at the top. For more transparency, I try to keep one unique module per file and name it resembling the full path of the file. This way also I never need to write a module without [], which is a common pain point.
I guess I do not completely understand the way require.js works. Here is a a simple module I've created:
requirejs.config({
paths: {
'underscore' : 'libs/underscore-min',
'backbone' : 'libs/backbone-min'
}
});
define([
"underscore",
"backbone"
], function(_, Backbone) {
console.log(_);
console.log(Backbone);
var MyCollection = Backbone.Collection.extend({
initialize: function() {
this.on("all", function(event) {
console.log(event);
});
}
});
return MyCollection;
});
I load it from my html:
<script data-main="js/mycollection.js" src="js/libs/require.min.js"></script>
The problem is it works intermittently. Sometimes Backbone object is there in the function when I need it, sometimes it doesn't (and gives me http://requirejs.org/docs/errors.html#notloaded error). If I just hit reload in the browser, I'd get 50/50 change of it working.
I must be missing something really basic here, the reported error doesn't make any sense to me, I thought the whole idea of the require.js mechanism is that my function will be called only when all the dependencies are loaded.
Since Underscore and Backbone haven't been defined as AMD modules, require.js does not know that Underscore is a dependency of Backbone. So I guess what happens in 50% of your cases is that Underscore isn't loaded when Backbone tries to use it.
You can use the require.js shim config http://requirejs.org/docs/api.html#config-shim to tell require.js about the dependency structure.
My guess is that you're not using an AMD version of underscore and Backbone. If that's the case and the two packages aren't wrapped as AMD modules - then the define function which is meant for modules won't work like it should.
For non-module js scripts, the more appropriate form would be to use the require() function.
OR - you can find the AMD version of underscore and Backbone here. AMD support was taken out of Underscore and Backbone at some point.
AMD Underscore
AMD Backbone