316

I have all of my AngularJS controllers in one file, controllers.js. This file is structured as follows:

angular.module('myApp.controllers', [])
  .controller('Ctrl1', ['$scope', '$http', function($scope, $http) {    
  }])
  .controller('Ctrl2', ['$scope', '$http', function($scope, $http) }
  }])

What I'd like to do is put Ctrl1 and Ctrl2 into separate files. I would then include both files in my index.html, but how should that be structured? I tried doing some thing like this and it throws an error in the web browser console saying it can't find my controllers. Any hints?

I searched StackOverflow and found this similar question - however, this syntax is using a different framework (CoffeeScript) on top of Angular, and so I haven't been able to follow.


AngularJS: How do I create controllers in multiple files

Community
  • 1
  • 1
Beebunny
  • 4,190
  • 4
  • 24
  • 37

6 Answers6

399

File one:

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

File two:

angular.module('myApp.controllers').controller('Ctrl1', ['$scope', '$http', function($scope, $http){

}]);

File three:

angular.module('myApp.controllers').controller('Ctrl2', ['$scope', '$http', function($scope, $http){

}]);

Include in that order. I recommend 3 files so the module declaration is on its own.


As for folder structure there are many many many opinions on the subject, but these two are pretty good

https://github.com/angular/angular-seed

http://briantford.com/blog/huuuuuge-angular-apps.html

Fresheyeball
  • 29,567
  • 20
  • 102
  • 164
  • 1
    If the OP indicated confusion about CoffeeScript syntax, maybe it would be best not to use it in your answer? – Andrew Mar 10 '14 at 14:08
  • @Fresheyeball, cool. I realize the question has come and gone, but it confused me for a brief second, so maybe this will help out future viewers. – Andrew Mar 10 '14 at 16:12
  • 3
    @Andrew imho future help and making a record of solutions is what SO is really all about, not extemporaneous q and a. – Fresheyeball Mar 10 '14 at 16:13
  • File one: `var appCtrl = angular.module('myApp.controllers', []);`File two: `appCtrl.controller('Ctrlr1');` File three: `appCtrl.controller('Ctrlr2');` – Ruslan Ismagilov Mar 20 '14 at 08:15
  • 2
    @RuslanIsmagilov your `appCtrl` is a global `window.appCtrl`. That is not a good practice. – Fresheyeball Mar 20 '14 at 16:01
  • 1
    @Fresheyeball, the problem of this approach is which the order of import in the index.html is important, otherwise, Angular emit error. – Deoxyseia Oct 10 '14 at 02:30
  • @Deoxyseia so put your files in order. Whats the problem? – Fresheyeball Oct 10 '14 at 06:06
  • The problem is which many yeoman generator import automatically the js files, without specific order. I think which a better solutions is define all items as modules. By example http://scotch.io/tutorials/javascript/node-and-angular-to-do-app-controllers-and-services. – Deoxyseia Oct 12 '14 at 16:07
  • That sounds like a problem with yeoman. It's an anti-pattern to alter code structure because of problems introduced by tooling. – Fresheyeball Oct 12 '14 at 17:09
  • why use 'myApp.controllers' instead of just 'myApp' as the module name? – hendryau Jan 19 '15 at 00:26
  • 2
    @hendryau, well I was working with the module name present in the OP. That said, some feel that its better organizationally, to have multiple name-spaced modules, rather than a central app module. – Fresheyeball Jan 20 '15 at 05:17
  • @Fresheyeball, Just as you mentioned I have created each controller in a seperate js file. All the controllers are placed in a Controller folder at the root. When my views are loaded the corresponding controllers are not attached. Each view is contained in a
    ........
    . Am I missing something?
    – sham Feb 25 '15 at 17:14
  • @sham, I would need to see the structure of your html. The problem you are having doesn't sound like it has anything to do with the controllers being in separate files, but rather instantiating with your views. Feel free to post a new question and paste a link to it here. I will come help. – Fresheyeball Feb 25 '15 at 23:13
  • @Fresheyeball, this is the link http://stackoverflow.com/questions/28727173/how-to-bind-controllers-contained-in-a-folder-to-views-in-angularjs-dynamically/28727360?noredirect=1#comment45744652_28727360 – sham Feb 26 '15 at 03:40
  • In this case, what would be the name of File one? I've not found a good naming-convention. – Jim Aho Apr 07 '15 at 14:24
  • I've been calling an analog to `File one`, `modules.js`, and using this file only to declare modules and manage dependencies. – Fresheyeball Apr 07 '15 at 14:57
  • How does this work if I have ``? – Robert Johnstone Sep 14 '15 at 15:21
  • This method should not be effected by representation in the dom. – Fresheyeball Sep 15 '15 at 05:11
  • How to avoid load all the controllers files in the master page? – Pablo Dec 02 '15 at 17:08
  • @Pablo 2 choices: use a lazy loader like `require` to load as needed, concat them into one file using a build tool like `gulp`. – Fresheyeball Dec 02 '15 at 17:17
  • Can you do the same to split `'Ctrl1'` in two separated files if that controller is becoming to big? – Juan Carlos Oropeza Sep 08 '16 at 03:15
  • @JuanCarlosOropeza if your controller is getting to big, I advise you to revisit your application design. Controllers should not be that big. – Fresheyeball Sep 08 '16 at 04:36
  • Where should the code for `app.config` and app.run` should go? the app.js or the controller.js file? or something else? my first app file contains only `var app = angular.module("test", ["ngRoute"]);` . Any help is appreciated. – FreeKrishna Jun 08 '17 at 14:08
  • This example guided me enough: https://github.com/angular/angular-seed First it failed because I forgot to add into index.html – JeanCarlos Chavarria Mar 04 '20 at 15:41
178

Using the angular.module API with an array at the end will tell angular to create a new module:

myApp.js

// It is like saying "create a new module"
angular.module('myApp.controllers', []); // Notice the empty array at the end here

Using it without the array is actually a getter function. So to seperate your controllers, you can do:

Ctrl1.js

// It is just like saying "get this module and create a controller"
angular.module('myApp.controllers').controller('Ctrlr1', ['$scope', '$http', function($scope, $http) {}]);

Ctrl2.js

angular.module('myApp.controllers').controller('Ctrlr2', ['$scope', '$http', function($scope, $http) {}]);

During your javascript imports, just make sure myApp.js is after AngularJS but before any controllers / services / etc...otherwise angular won't be able to initialize your controllers.

Jimmy Au
  • 2,065
  • 1
  • 13
  • 14
  • where should i write my dependencies. var myapp = angular.module('demo', ['ngRoute','ngCookies','ui.bootstrap','nvd3ChartDirectives','ui-rangeSlider','textAngular','angularTreeview']); – vipin Aug 05 '14 at 07:10
  • @vipin just like what you've typed, but make sure it's above any controllers, services, etc. Technically you don't need to declare var myapp = ...; because angular will store it for you. – Jimmy Au Aug 18 '14 at 10:20
  • @JimmyAu Where does Ctrl1.js and Ctrl2.js get loaded so that the page can use it? I have myApp.js loaded just after angular, but the page can't find the controllers. Do I have to explicitly add them as a script on the view that needs it? Or do I still have to include every controller file on every page? – Sinaesthetic Dec 14 '15 at 00:39
  • 2
    Thanks for clarifying why only the first call needs []. – Jim B. Jan 10 '16 at 07:07
51

Although both answers are technically correct, I want to introduce a different syntax choice for this answer. This imho makes it easier to read what's going on with injection, differentiate between etc.

File One

// Create the module that deals with controllers
angular.module('myApp.controllers', []);

File Two

// Here we get the module we created in file one
angular.module('myApp.controllers')

// We are adding a function called Ctrl1
// to the module we got in the line above
.controller('Ctrl1', Ctrl1);

// Inject my dependencies
Ctrl1.$inject = ['$scope', '$http'];

// Now create our controller function with all necessary logic
function Ctrl1($scope, $http) {
  // Logic here
}

File Three

// Here we get the module we created in file one
angular.module('myApp.controllers')

// We are adding a function called Ctrl2
// to the module we got in the line above
.controller('Ctrl2', Ctrl2);

// Inject my dependencies
Ctrl2.$inject = ['$scope', '$http'];

// Now create our controller function with all necessary logic
function Ctrl2($scope, $http) {
  // Logic here
}
thank_you
  • 11,001
  • 19
  • 101
  • 185
  • Interesting, it does keep me from going to multiple files to register a controller – mrwaim Aug 17 '15 at 00:43
  • 4
    I see a lots of coding like this. What is the advantage? of having $inject and a function seperate. – Alaksandar Jesus Gene Aug 27 '15 at 06:59
  • 2
    I believe it makes the code easier to read. I know what exactly is being injected. Think of it as a "separation of concerns" on a line-by-line basis. – thank_you Dec 03 '15 at 05:23
  • 2
    Code like this not only produces more readable code, is much easier to debug, and reduces the amount of nested callback code (see https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#named-vs-anonymous-functions) – rfornal Dec 29 '16 at 14:21
  • If I could +1 this 1000 times I would - bravo! – Dan Chase May 27 '17 at 21:29
17

What about this solution? Modules and Controllers in Files (at the end of the page) It works with multiple controllers, directives and so on:

app.js

var app = angular.module("myApp", ['deps']);

myCtrl.js

app.controller("myCtrl", function($scope) { ..});

html

<script src="app.js"></script>
<script src="myCtrl.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">

Google has also a Best Practice Recommendations for Angular App Structure I really like to group by context. Not all the html in one folder, but for example all files for login (html, css, app.js,controller.js and so on). So if I work on a module, all the directives are easier to find.

Ahmed Mansour
  • 527
  • 5
  • 13
schasoli
  • 317
  • 1
  • 3
  • 12
4

For brevity, here's an ES2015 sample that doesn't rely on global variables

// controllers/example-controller.js

export const ExampleControllerName = "ExampleController"
export const ExampleController = ($scope) => {
  // something... 
}

// controllers/another-controller.js

export const AnotherControllerName = "AnotherController"
export const AnotherController = ($scope) => {
  // functionality... 
}

// app.js

import angular from "angular";

import {
  ExampleControllerName,
  ExampleController
} = "./controllers/example-controller";

import {
  AnotherControllerName,
  AnotherController
} = "./controllers/another-controller";

angular.module("myApp", [/* deps */])
  .controller(ExampleControllerName, ExampleController)
  .controller(AnotherControllerName, AnotherController)
Pete TNT
  • 8,293
  • 4
  • 36
  • 45
  • 1
    You could save quite bit of typing if you used named functions.. they have handy property `name`.. so you can simply use `ExampleCtrl.name` instead of dupl.. triplicating it. – Antti Pihlaja Nov 21 '16 at 10:07
  • I cannot make it work. https://plnkr.co/edit/Wxf4954ozNQEEfXU?open=lib%2Fscript.js&deferRun=1 - Module 'myApp' is not available! – Darius.V Jan 19 '21 at 16:07
0

Not so graceful, but the very much simple in implementation solution - using global variable.

In the "first" file:


window.myApp = angular.module("myApp", [])
....

in the "second" , "third", etc:


myApp.controller('MyController', function($scope) {
    .... 
    }); 
user3682640
  • 168
  • 1
  • 3
  • i use this code but still cannot load my controller ? it throw error: Error: [ng:areq] Argument 'ProductCtrl' is not a function, got undefined. – QViet Jul 26 '15 at 14:49
  • 7
    this is really bad practice – Brendan W Aug 20 '15 at 14:18
  • @Kim Jong Un You will see that error if you don't add/concatenate the controller to the module that you created. So it will work if you use the following syntax: `angular.module('myApp').controller('ProductCtrl', ['$scope', '$http', function($scope, $http){ //Your ProductCtrl code goes here }]);` – Devner Jan 04 '16 at 06:46
  • 1
    @Brendan, simply stating that something is bad practise is better than nothing - but not much. Tell us ***why*** it is bad practise will help others. – Mawg says reinstate Monica Oct 09 '17 at 15:25