6

I have been writing a angularjs app and i have a number of controllers which I have placed in a 1 JS file. I was hoping for something a little more modular and separating my controllers into there own files.

I thought of RequireJS but is this the recommended way? or does angularjs provide something else and any clue on where it is explained?

Also having all these files is great for debugging but once a production build is required does angularJS provide some sort of merging of of the modules into 1 file and minimizing the results?

If anyone can explain the best way to go about it it would be very helpful.

Thanks

Martin
  • 23,844
  • 55
  • 201
  • 327

4 Answers4

12

Angular dependencies injection is really great and you should create a lot of small modules.

When it comes to file organization it's way easier to have a lot a small files (one per module maybe), but then you face the issue you're talking about: what do I do with all these files? How do I load them all?

It is worth taking a look at these 2 sources: Brian Ford's blog and this Github repo. It helped me a lot improving my workflow and better understand/use Angular modules.

TL;DR

What I do for my projects is using Grunt to concat (minify if needed) all the js files (and way more: less css compilation, assets management, javascript templates compilation).
A good example is given in the Github repo above.

I don't recommend using RequireJS with AngularJS. Although it's certainly possible, I haven't seen any instance where RequireJS was beneficial in practice. [Brian Ford]

Files organization

My app folder looks like this:

www
|-dist/         = Created by Grunt. Generated files (served by my web server).
|-node_modules/ = node modules (ie. Grunt modules. Grunt is based on NodeJS)
|-src/          = My workspace
|-test/         = Some tests
|-vendor        = External libraries (AngularJS, JQuery, Bootstrap, D3, ... Whatever you need)
|-gruntFile.js  = The Grunt configuration file: contains all the jobs for Grunt.
|-package.json  = App infos + dependencies (Grunt modules) I use (concat, uglify, jshint, ...)

So all the different files I work on are in the src folder which then looks like this:

www/src
|-app              = Folder for my controllers
| |-Controller1.js
| |-Controller2.js
| |-...
|-assets           = Folder for the static assets (img, css, ...)
| |-img/
| |-css/
| |-favicon.ico
|-common           = Folder for the shared modules (directives, resources, services, ...)
| |-directives
| | |-chart.js
| | |-map.js
| | |-...
| |-resources
| | |-users.js
| | |-another-cool-resource.js
| | |-...
| |-services
| | |-service1.js
| | |-...
|-views            = Folder for my templates
| |-profile.tpl.html
| |-search.tpl.html
| |-...
|-index.html       = The main and unique html file.

Grunt

Then I use Grunt to 'compile' everything into the dist folder.
An example of a gruntFile can be found here. I have a one-time job for deployment and some watchers for development.

Do you need more explanation?

maxdec
  • 5,707
  • 2
  • 30
  • 35
  • Really good explanation and really good pointers... Thanks for the links.. I am going to try it now. – Martin Apr 09 '13 at 08:55
  • 3
    Using RequireJS has many advantages (parallel downloading, caching, ...). What I like about it is that I can control what part of my application is loaded or not. Why loading the whole app if your users only access some section (based on user rights for instance). So I wouldn't say that RequireJS is not useful. It is useful in specific instance indeed... – nolazybits Apr 14 '13 at 23:24
  • @zeflasher: I agree with you. But you still need to to balance between "loading one big file (with some useless parts)" and "load several smaller ones and do more requests". Loading the big one can still be quicker. It depends on your application. – maxdec Apr 15 '13 at 09:07
  • @maxdec please see Chandra answer. RequireJS helps you to load on demand only, so saving bandwidth if the user will never use a part of your app – nolazybits Apr 26 '13 at 01:03
  • 1
    If you make applications that are in daily use, large and high traffic is preferable to use requierejs If it is a simple application which does not have many features, it is best not to use requires. – AURIGADL May 31 '13 at 17:35
  • Or if you use AngularJS in a HTML5 mobile app you can avoid requirejs too, as all the files are already on the device. – maxdec Jun 03 '13 at 07:51
3

Angular allows you to define modules which can be injected into your controllers see here:

http://docs.angularjs.org/api/angular.module

These modules can be dependent on other modules which can be injected like this:

app.factory('module2', ['module1', function (module1) {
   var functions = {
       myModule2Function: function (variable) {
          return  module1.testfunction(variable);
       }
   }
    return functions;
}]);

then in the controller:

function MyController($scope, module2) { 
      $scope.aControllerFunction = function (variable) {
         return module2.myModule2Function(variable);
     }

}
Richard
  • 21,728
  • 13
  • 62
  • 101
  • Great thanks, these modules can be stored in separate files (separate js files??)?? How does one module know where to find the physical file? – Martin Mar 12 '13 at 16:07
  • They can be stored in separate files but you would usually load all these files at runtime. We use a bundling system to minify all modules and controllers into one js file for live deployment. I have heard of people writing require.js plugins to load the modules but have not found the need for it myself. – Richard Mar 12 '13 at 16:23
  • Thanks Richard, So I would have to link the JS files in the HTML at runtime?? What bundling do you use out of interest? Would be great if in development i could use all individual js files (many) and then production use just 1 bundled min version – Martin Mar 13 '13 at 12:39
  • We work with MVC4 and use the web optimzation framework http://aspnetoptimization.codeplex.com/documentation but there are others you can use for different platforms. such as those for rails: https://github.com/blog/551-optimizing-asset-bundling-and-serving-with-rails be careful to make your code minification safe though :http://stackoverflow.com/questions/14909541/mvc4-bundling-minification-and-angularjs-services – Richard Mar 13 '13 at 12:45
2

Load modules on demand is what we need for really huge apps, like a trading application for instance. I think that's a smart way to handle behaviors at least on mobile devices where we don't want to load the entire app on a 3G connection. In those cases, RequireJS fits best.

Chandra
  • 505
  • 1
  • 6
  • 10
0

Using RequireJS with AngularJS makes sense but only if you understand how each of them works regarding dependency injection, as although both of them injects dependencies, they inject very different things.

AngularJS has its own dependency system that let you inject AngularJS modules to a newly created module in order to reuse implementations. Let's say you created a "first" module that implements an AngularJS filter "greet":

angular
  .module('first', [])
  .filter('greet', function() {
    return function(name) {
      return 'Hello, ' + name + '!';
    }
  });

And now let's say you want to use the "greet" filter in another module called "second" that implements a "goodbye" filter. You may do that injecting the "first" module to the "second" module:

angular
  .module('second', ['first'])
  .filter('goodbye', function() {
    return function(name) {
      return 'Good bye, ' + name + '!';
    }
  });

The thing is that in order to make this work correctly without RequireJS, you have to make sure that the "first" AngularJS module is loaded on the page before you create the "second" AngularJS module. Quoting documentation:

Depending on a module implies that required module needs to be loaded before the requiring module is loaded.

In that sense, here is where RequireJS can help you as RequireJS provides a clean way to inject scripts to the page helping you organize script dependencies between each other.

Going back to the "first" and "second" AngularJS modules, here is how you can do it using RequireJS separating the modules on different files to leverage script dependencies loading:

// firstModule.js file
define(['angular'], function(angular) {
  angular
    .module('first', [])
    .filter('greet', function() {
      return function(name) {
        return 'Hello, ' + name + '!';
      }
    });
});
// secondModule.js file
define(['angular', 'firstModule'], function(angular) {
  angular
    .module('second', ['first'])
    .filter('goodbye', function() {
      return function(name) {
        return 'Good bye, ' + name + '!';
      }
    });
});

You can see that we are depending on "firstModule" file to be injected before the content of the RequireJS callback can be executed which needs "first" AngularJS module to be loaded to create "second" AngularJS module.

Side note: Injecting "angular" on the "firstModule" and "secondModule" files as dependency is required in order to use AngularJS inside the RequireJS callback function and it have to be configured on RequireJS config to map "angular" to the library code. You may have AngularJS loaded to the page in a traditional manner too (script tag) although defeats RequireJS benefits.

More details on having RequireJS support from AngularJS core from 2.0 version on my blog post.

Based on my blog post "Making sense of RequireJS with AngularJS", here is the link.

leog
  • 778
  • 10
  • 15