93

I'm using the angular-seed template to structure my application. Initially I put all my JavaScript code into a single file, main.js. This file contained my module declaration, controllers, directives, filters, and services. The application works fine like this, but I'm worried about scalability and maintainability as my application becomes more complex. I noticed that the angular-seed template has separate files for each of these, so I've attempted to distribute my code from the single main.js file into each of the other files mentioned in the title to this question and found in the app/js directory of the angular-seed template.

My question is: how do I manage the dependencies to get the application to work? The existing documentation found here isn't very clear in this regard since each of the examples given shows a single JavaScript source file.

An example of what I have is:

app.js

angular.module('myApp', 
    ['myApp.filters',
     'myApp.services',
     'myApp.controllers']);

controllers.js

angular.module('myApp.controllers', []).
    controller('AppCtrl', [function ($scope, $http, $filter, MyService) {

        $scope.myService = MyService; // found in services.js

        // other functions...
    }
]);

filters.js

angular.module('myApp.filters', []).
    filter('myFilter', [function (MyService) {
        return function(value) {
            if (MyService.data) { // test to ensure service is loaded
                for (var i = 0; i < MyService.data.length; i++) {
                    // code to return appropriate value from MyService
                }
            }
        }
    }]
);

services.js

angular.module('myApp.services', []).
    factory('MyService', function($http) {
        var MyService = {};
        $http.get('resources/data.json').success(function(response) {
            MyService.data = response;
        });
        return MyService;
    }
);

main.js

/* This is the single file I want to separate into the others */
var myApp = angular.module('myApp'), []);

myApp.factory('MyService', function($http) {
    // same code as in services.js
}

myApp.filter('myFilter', function(MyService) {
    // same code as in filters.js
}

function AppCtrl ($scope, $http, $filter, MyService) {
    // same code as in app.js
}

How do I manage the dependencies?

starball
  • 20,030
  • 7
  • 43
  • 238
ChrisDevo
  • 1,222
  • 2
  • 13
  • 17
  • 2
    there are some articles about this. for example, http://briantford.com/blog/huuuuuge-angular-apps.html, and some projects, like yeoman. thereare two ways to approach this: separating components by layers (like angular-seed) or by feature, like some articles suggest. – Eduard Gamonal May 27 '13 at 13:00
  • When you declare a module e.g. **angular.module('myApp.service1', [])** I think you need to add dependencies to the **[]** e.g. **angular.module('myApp.service1', ['myApp.service2', 'myApp.service2']).**. I'm pretty new to AngularJS, so I may be wrong. If this works out, let me know and I'll post an answer. – Danny Varod May 27 '13 at 14:43
  • Where should that line be inserted? I've put `angular.module('myApp.services', ['myApp.services']);` into app.js with no luck. – ChrisDevo May 27 '13 at 14:49
  • @ChrisDevo 1. When you reply to comments, always start the comment with '@' and the user's name (without spaces), so the user will get a notification. 2. myApp.services should be dependent on other modules, not on itself, e.g. `angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse'])`. – Danny Varod May 28 '13 at 12:38
  • @DannyVarod, did you edit your previous comment? I read it as `angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])` originally. Otherwise I wouldn't have included myApp.services as its own dependency. – ChrisDevo May 28 '13 at 13:10
  • @ChrisDevo Only to add the last sentence. – Danny Varod May 29 '13 at 09:12
  • @DannyVarod Then why would you list myApp.service2 twice? In any case, I wasn't trying to add dependencies between services, only to the main app. So a more helpful example would be exactly like the one found in my app.js code. – ChrisDevo May 29 '13 at 09:46
  • More or less the same, just add the dependencies to the app's definition. – Danny Varod May 29 '13 at 21:59
  • Found the problem: AppCtrl depends on MyService. Solution add dependency 'myApp.services' to 'myApp.controllers'. – Danny Varod May 29 '13 at 22:29
  • @DannyVarod Actually, there is no need to add that dependency as long as the html file lists the source code for the service in a script tag. – ChrisDevo May 29 '13 at 23:05
  • Does that mean you've tried it? – Danny Varod May 29 '13 at 23:20
  • @DannyVarod I have. None of my files have dependencies listed between the square brackets. As long as I use the controller, service, filter, or directive name found in their respective declarations, then I can use them anywhere and AngularJS will find them (provided the source code containing the reference is found in an html script tag on the relevant page). – ChrisDevo May 30 '13 at 06:16

3 Answers3

108

The problem is caused from you "redeclaring" your application module in all your separate files.

This is what the app module declaration (not sure declaration is the right term) looks like:

angular.module('myApp', []).controller( //...

This is what assignment (not sure if assignment is the right term either) to your application module looks like:

angular.module('myApp').controller( //...

Notice the lack of square brackets.

So, the former version, one with the square brackets, should only be used once, usually in your app.js or main.js. All other associated files — controllers, directives, filters … — should use the latter version, the one without the square brackets.

I hope that makes sense. Cheers!

Danger14
  • 760
  • 2
  • 12
  • 33
Justin L.
  • 1,276
  • 1
  • 8
  • 5
  • Thanks, Justin, but that doesn't seem to work either. Although it does give me some new errors: `Uncaught Error: No module: myApp.controllers`, `Uncaught Error: No module: myApp.filters` (twice), `Uncaught Error: No module: myApp.services`. – ChrisDevo May 28 '13 at 08:10
  • 9
    I have found a solution to this issue. It seems that only one module is needed `myApp`. In my app.js file I created a var for it `var myApp = angular.module('myApp', []);` In all the other files I simply referred to this var (e.g., `myApp.filter('myFilter', function(MyService) { /* filter code */ });` As long as I added the file names in script tags in my html, I didn't need to declare any other dependencies. The angular-seed project template is quite confusing in this regard. – ChrisDevo May 28 '13 at 10:27
  • 1
    Now I'm really confused. I've gone back and removed the global var `myApp`, so that it is now an anonymous module in `app.js`: `angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);`. In all the other files I also declared anonymous modules like this `angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });`. My app now works like this too. I've gone over my code history and I can't see where this is any different than what I when it didn't work. – ChrisDevo May 28 '13 at 11:18
  • 4
    I promise you this works. I use it every single day developing Angular Apps. Again, I have one declaration of `angular.module('myApp', []);` (notice the square brackets) in my main js file. Then, all other files refer to the module by using the `angular.module('myApp').controller` or `.directive` syntax. – Justin L. May 30 '13 at 22:01
  • 5
    The errors are caused because Angular is looking for modules called myApp.controller, myApp.filters … but, those are not modules, they are components extending the one module called myApp. Everything should work if you remove all your myApp.whatever from your module dependencies. Just keep the square brackets empty (unless you are including other true angular modules, e.g. `ngCookies`, `ngResource`) when declaring your main app module: `angular.module('myApp', []);` – Justin L. May 30 '13 at 22:07
  • Thanks, I figured this out already, yet I appreciate your clarification. I'm not sure why the angular-seed template sets up all the dependencies like it does. As it turns out my original error wasn't really a dependency issue. It was the result of calling my service without ensuring that it was loaded (and thus defined). Now I need to figure out how to use ngResource I think. – ChrisDevo May 31 '13 at 07:16
  • 1
    Ah, okay. By placing those as a dependency for your app module, it will error because it *has* to find them. If you take them out of the square brackets, you can load them at will and your app won't care. `ngResource` is definitely one of those you place in your module declaration brackets. If my answer did help, voting and approving it would be much appreciated. – Justin L. May 31 '13 at 18:35
  • 1
    I certainly appreciate your help. – ChrisDevo Jun 01 '13 at 02:01
  • are you looking for this? http://stackoverflow.com/questions/12655890/angularjs-how-do-i-create-controllers-in-multiple-files – zx1986 Sep 24 '13 at 07:50
  • Thank you for this great answer that saved my day. I prefer having a separate file/module declaration for dependencies `angular.module('app.submodule', ['dependency1', 'dependency2');`, as I'm not comfortable leaving that responsibility on the controller – Martin Folkeseth Aug 27 '15 at 10:40
12

If you're wanting to put your different parts of your application (filters, services, controllers) in different physical files, there are two things you have to do:

  1. Declare those namespaces (for lack of a better term) in your app.js or in each file;
  2. Refer to those namespaces in each of the files.

So, your app.js would look like this:

angular.module('myApp', ['external-dependency-1', 'myApp.services', 'myApp.controllers'])
.run(function() {
   //...

})
.config(function() {
  //...
});

And in each individual file:

services.js

angular.module('myApp.services', []); //instantiates
angular.module('myApp.services') //gets
.factory('MyService', function() { 
  return {};
});

controllers.js

angular.module('myApp.controllers', []); //instantiates
angular.module('myApp.controllers')      //gets
.controller('MyCtrl', function($scope) {
  //snip...
})
.controller('AccountCtrl', function($scope) {
  //snip...
});

All of this can be combined into one call:

controllers.js
angular.module('myApp.controllers', []) 
.controller('MyCtrl', function($scope) {
 //snip...
});    

The important part is that you shouldn't redefine angular.module('myApp'); that would cause it to be overwritten when you instantiate your controllers, probably not what you want.

Community
  • 1
  • 1
George Stocker
  • 57,289
  • 29
  • 176
  • 237
  • 1
    I like this approach, you can use it to create a tiered structure (say your controller needs a service or filter). Note, I've also found that once a sub-dependency (like that filter for a controller) is injected, it is also available in all parents above (if visualizing as a tree) without, re-declaring the injection, just don't forget about it if you change the child and are using it in the parent. Also I would recommend never putting more than one module (namespace) in a file and never try to use the same namespace in multiple files. – Gary May 04 '16 at 13:17
  • What if I have two controller files or two services files? Does the instantiate would be happen for each single file? – Avinash Raj Oct 19 '16 at 06:47
3

You get the error because you didn't define myApp.services yet. What I did so far is putting all the initial definitions in one file and then use them in another. Like for your example I would put in:

app.js

angular.module('myApp.services', []);

angular.module('myApp', 
    ['myApp.services',
      ...]);

That should get rid of the error, though I think you should have a read on the article Eduard Gamonal mentioned in one of the comments.

Community
  • 1
  • 1
Torsten Engelbrecht
  • 13,318
  • 4
  • 46
  • 48
  • Thanks, Torsten, but I've read that article and added the line `angular.module('myApp.services', []);` to my app.js file. Neither really addresses my problem. – ChrisDevo May 27 '13 at 14:39