How to create separate AngularJS controller files?
Asked Answered
E

6

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

Eyecup answered 20/11, 2013 at 4:51 Comment(0)
P
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

Paratrooper answered 20/11, 2013 at 4:55 Comment(24)
If the OP indicated confusion about CoffeeScript syntax, maybe it would be best not to use it in your answer?Reptant
@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.Reptant
@Reptant imho future help and making a record of solutions is what SO is really all about, not extemporaneous q and a.Paratrooper
File one: var appCtrl = angular.module('myApp.controllers', []);File two: appCtrl.controller('Ctrlr1'); File three: appCtrl.controller('Ctrlr2');Restless
@RuslanIsmagilov your appCtrl is a global window.appCtrl. That is not a good practice.Paratrooper
@Fresheyeball, the problem of this approach is which the order of import in the index.html is important, otherwise, Angular emit error.Actiniform
@Actiniform so put your files in order. Whats the problem?Paratrooper
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 scotch.io/tutorials/javascript/….Actiniform
That sounds like a problem with yeoman. It's an anti-pattern to alter code structure because of problems introduced by tooling.Paratrooper
why use 'myApp.controllers' instead of just 'myApp' as the module name?Yancey
@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.Paratrooper
@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 <div ng-controller="loginController"> ........</div>. Am I missing something?Insulting
@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.Paratrooper
@Fresheyeball, this is the link #28727673Insulting
In this case, what would be the name of File one? I've not found a good naming-convention.Prerogative
I've been calling an analog to File one, modules.js, and using this file only to declare modules and manage dependencies.Paratrooper
How does this work if I have <html ng-app="..." ng-controller="Ctrl1">?Vocoid
This method should not be effected by representation in the dom.Paratrooper
How to avoid load all the controllers files in the master page?Baroque
@Baroque 2 choices: use a lazy loader like require to load as needed, concat them into one file using a build tool like gulp.Paratrooper
Can you do the same to split 'Ctrl1' in two separated files if that controller is becoming to big?Emplane
@JuanCarlosOropeza if your controller is getting to big, I advise you to revisit your application design. Controllers should not be that big.Paratrooper
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.Kef
This example guided me enough: github.com/angular/angular-seed First it failed because I forgot to add <script scr="myfile1.js"></script> into index.htmlIlonailonka
H
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.

Hoe answered 20/11, 2013 at 5:39 Comment(4)
where should i write my dependencies. var myapp = angular.module('demo', ['ngRoute','ngCookies','ui.bootstrap','nvd3ChartDirectives','ui-rangeSlider','textAngular','angularTreeview']);Garling
@Garling 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.Hoe
@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?Warr
Thanks for clarifying why only the first call needs [].Rogation
T
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
}
Thatch answered 21/1, 2015 at 4:3 Comment(5)
Interesting, it does keep me from going to multiple files to register a controllerWeiler
I see a lots of coding like this. What is the advantage? of having $inject and a function seperate.Restriction
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.Thatch
Code like this not only produces more readable code, is much easier to debug, and reduces the amount of nested callback code (see github.com/johnpapa/angular-styleguide/blob/master/a1/…)Hartle
If I could +1 this 1000 times I would - bravo!Socialize
B
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.

Bondy answered 20/2, 2016 at 0:32 Comment(0)
U
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)
Uncircumcision answered 13/7, 2016 at 8:1 Comment(2)
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.Horgan
I cannot make it work. plnkr.co/edit/… - Module 'myApp' is not available!Duodiode
L
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) {
    .... 
    }); 
Lablab answered 4/6, 2015 at 12:18 Comment(4)
i use this code but still cannot load my controller ? it throw error: Error: [ng:areq] Argument 'ProductCtrl' is not a function, got undefined.Tundra
this is really bad practiceLegitimatize
@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 }]);Paisano
@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.Vasiliu

© 2022 - 2024 — McMap. All rights reserved.