While this thread sums up the following three code styles:
1)
angular.module('mainCtrl', []);
function MainCrl($scope, $rootScope) {}
2)
angular.module('mainCtrl',[])
.controller('MainCtrl', function($scope, $rootScope)) { ... });
3)
angular.module('mainCtrl',[])
.controller('MainCtrl', ['$scope', '$rootScope', function(scope, rootScope)) { ... }]);
there's a fourth way i've seen in this video that is very appealing for me
4)
var controllers = {}
controllers.mainCtrl = function($scope, $rootScope){ };
app.controller(controllers)
I am leaning towards continuing with 4), will it break if minified or are there any other drawbacks? Should i just go with 3) since it seems to be the standard way of doing it?
Option 1 pollutes global namespace and hinders minification and does not respect modules.
Option 2 does not let you rename your injectables in controller signature.
Option 4 pollutes global namespace, but it is minification-safe if you do it properly1.
Option 3 lets you rename your injectables2, does respect modules, does not pollute global namespace, and does not require any extra work when minifying.
So my winner is option #3.
1 Option 4 - minification-friendly version:
var controllers = {};
controllers.mainCtrl = ['$scope', '$rootScope', function($scope, $rootScope){ ... }];
app.controller(controllers);
2 Renaming injectables:
app.controller('MyCtrl', ['$scope', 'UserService', function($scope, User){ ... }]);
My recommendation: Go with option 3, for three reasons:
It is (IMHO) the most widely adopted one.
You have no problems with minification (and this is the only option where this is true).
It works best with modules.
Related
I see the following angularjs controller syntax structure all the time.
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ($scope, $interval, $location)
{
}]);
Why the repetition in the parameter names? Why not just like this
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location',
function ()
{
}]);
or
angular.module('7minWorkout').controller('WorkoutController',
[
function ($scope, $interval, $location)
{
}]);
The array syntax will help you with minification/uglify of your js code.
angular.module('7minWorkout').controller('WorkoutController',
function ($scope, $interval, $location) {
// code here
});
Will be minified and mangled as:
angular.module('7minWorkout').controller('WorkoutController',
function (a, b, c) {
// code here
});
So Angular won't be able to figure out which dependencies to inject
On the other hand, using the array declaration:
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function ($scope, $interval, $location) {
// code here
}]);
Will be minified as :
angular.module('7minWorkout').controller('WorkoutController',
['$scope', '$interval', '$location', function (a, b, c) {
// code here
}]);
So Angular will know what a, b and c represent.
There's also another way to inject variables if you use your first example code like below:
WorkoutController.$inject = ['$scope', '$interval', '$location'];
or
angular.module('7minWorkout').controller('WorkoutController', /* #ngInject */
function ($scope, $interval, $location) {
// code here
});
which will create the $inject method mentioned above when the code is annotated.
So, there are mainly four kinds of annotation:
Implicit Annotation - the first example code
Inline Array Annotation - the second example code
$inject Property Annotation - the $inject method
$ngInject Comment Annotation - the #ngInject method
ng-annotate
Tools like ng-annotate let you use implicit dependency annotations in your app and automatically add inline array annotations prior to minifying. If you decide to take this approach, you probably want to use ng-strict-di.
For more information, see AngularJS Developer Guide - Using Strict Dependency Injection.
This "repetion" is to make it safe for minification:
AngularJS - Controllers, Dependencies, and Minification
or you can use following syntax, according to popular angular-styleguide https://github.com/johnpapa/angular-styleguide
angular.module('7minWorkout')
.controller('WorkoutController', WorkoutController);
WorkoutController.$inject = ['$scope', '$interval', '$location'];
function WorkoutController($scope, $interval, $location) {
}
You could write the first version since it just omits the parameters of the function which are also accesible via arguments inside the function. So you would avoid the repition but slicing the arguments property is also not really efficient (compared to just type out the parameters).
As the others answers stated the repition is to make it safe for minification.
The first controller syntax makes it possible to minify/uglify the javascript code with the use of tools like ngmin. I'm not quite sure if the 2nd and 3rd options you have provided are viable options to create a controller, but in any case they will not be minified correctly since the tools will not now what the providers are. I would either suggest to use option 1 or option 3 (without the brackets) to create a controller. Note that option 3 will not be minified correctly by automated tools.
Some Useful information about creating controllers:
AngularJS Developer Guide - Controllers
option3 without brackets
angular.module('7minWorkout').controller('WorkoutController', function ($scope, $interval, $location)
{
//Your Code
});
I had observed that inside AngularSeed, some controllers have the following format:
angular.module('myApp.controllers', []).
controller('MyCtrl1', [function() {
}])
.controller('MyCtrl2', [function() {
}]);
whereas, some controllers have the following syntax:
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl1', ['$scope', function($scope) {
} }]);
myApp.controller('MyCtrl2', ['$scope', function($scope) {
} }]);
Which is a good practice for a project in production?
Also, is there any performance difference between these two approaches?
There will be no difference in the performance among these two syntaxes.
But it is recommended that we use the second approach.
Remember that angular.module() returns a module object. This would expose the functions of controllers, filters, services, and directive registrations.
Now each of these functions would return the same module, so we are talking about the same reference. This is similar to a builder pattern.
According to Angular Best Practice for App Structure (Public), it is recommended that angular.module() should not be called more than once, and other files and modules should not modify the same.
For this reason, the latter is always recommended.
The logic is simple:
Expose your module as a global variable, and let other files add on to that variable.
if your project is very big i suggest you this syntax...
var controller = function (scope) {
};
controller.$inject = ["$scope"];
app.controller("appCtrl", controller);
When you include only a single component per file, there is rarely a need to introduce a variable for the module. Instead, use the simple getter syntax. When using a module, using chaining with the getter syntax avoids variables collisions or leaks.
From John Papa's Angular style guide:
/* avoid */
var app = angular.module('app');
app.controller('SomeController' , SomeController);
function SomeController() { }
/* recommended */
angular
.module('app')
.controller('SomeController' , SomeController);
function SomeController() { }
I would like to understand the difference between the declaration of MyOtherService and MyOtherComplexService. Especially what is the purpose of square bracket part? When to use them and when not?
var myapp = angular.module('myapp', []);
myapp.factory('MyService', function($rootScope, $timeout) {
return {
foo: function() {
return "MyService";
}
}
});
myapp.factory('MyOtherService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherService";
}
}
});
myapp.factory('MyOtherComplexService', ['$rootScope', '$timeout', 'MyService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherComplexService";
}
}
}]);
myapp.controller('MyController', function($scope, MyOtherService, MyOtherComplexService) {
$scope.x = MyOtherService.foo();
$scope.y = MyOtherComplexService.foo();
});
It enables AngularJS code to be minified. AngularJS uses parameter names to inject the values to your controller function. In JavaScript minification process, these parameters are renamed to shorter strings. By telling which parameters are injected to the function with a string array, AngularJS can still inject the right values when the parameters are renamed.
To add to Ufuk's answer:
ngmin - compiles your standard modules to min-safe modules
Angular's min-safe square bracket notation is cleary less convenient, because you have to type every dependency twice and argument order matters. There is a tool called ngmin which compiles your standard modules to min-safe modules, so you don't have to manage all those things by hand.
Angular + CoffeeScript
If you're using CoffeeScript the situation is even worse. You may choose between ngmin, which will destroy your source map, or if you want to write it out all by yourself you'll have to wrap your entire code with square brackets, which is super ugly.
angular.module('whatever').controller 'MyCtrl', ['$scope', '$http' , ($scope, $http) ->
# wrapped code
]
In my opinion this is not a CoffeeScript flaw, but a poor design decision of the Angular team, because it's against all JS/CoffeeScript conventions not to have the function as the last argument. Enough ranting, here is a little helper function to work around it:
deps = (deps, fn) ->
deps.push fn
deps
This is a very simple function that accepts two arguments. The first one is an array of strings containing your dependencies, the second one is your module's function. You may use it like this:
angular.module('whatever').controller 'MyCtrl', deps ['$scope', '$http'] , ($scope, $http) ->
# unwrapped code \o/
Just to exemplify what was already said, if you use the following syntax:
myapp.factory('MyService', function($scope, $http, MyService) { ... });
most of the JS minifiers will change it to:
myapp.factory('MyService', function(a, b, c) { ... });
since functions argument names usually can be renamed for shorter names. This will break the Angular code.
In Angular, to get your code minifiable in all minifiers, you use the bracket syntax:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function($scope, $http, MyService) { ... }]);
that will be minified to:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function(a, b, c) { ... }]);
Note that minifiers do not touch on strings so Angular will see the minified code and match arguments in order:
$scope = a
$http = b
MyService = c
To avoid this ugly square bracket syntax, you should use smart minifiers like ng-annotate.
As of now, ng-min is deprecated.
Use ng-annotate instead.
It is good practice to use ng-annotate in your build job so you don't have to deal with the min-safe / bracket notation when developing, as it makes the code harder to read and maintain.
There is a grunt-plugin and a gulp plugin available on npm.
AngularJS supports two slightly different syntaxes for dependency injection
Syntax 1
myModule.controller('myCtrl', function($scope, $http, myService) {
...
...
});
Syntax 2
myModule.controller('myCtrl', ['$scope', '$http', 'myService', function($scope, $http, myService) {
...
...
}]);
Is there a fundamental difference between the two syntaxes?
When to use either of the two syntaxes?
Syntax 2 is called type hinting, if you plan to uglify and mangle your code, angular would still know what services to inject.
After mangling and ugilying:
myModule.controller('myCtrl', ['$scope', '$http', 'myService', function(a, b, c) {
Angular would read the string values of the array provided in order to determine the name of the services a, b, c in order to inject them properly.
I have looked at various examples of separating the files. I can understand them but the problem comes with the way my code is. I want separate these controllers in different files.
'use strict';
angular.module('myModule', ['ui.bootstrap']);
var myApp = angular.module('myApp', []);
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
})
The below controller needs to be in a different file.
///////////// MONTHLY DATA /////////////////////////////////////
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
.controller('MonthlyCtrl', function ($scope, $http) { $scope.visible = true;
})
I have more than 20 controllers like above. but how do I separate them.
Here is how you should do it,
first declaration
angular.module('appName', ['Module1', 'Module2', 'Service1']);
subsequent declarations
here all the controllers and service can be in separate files.
angular
.module('Module1', [])
.controller('AbcController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular
.module('Module2', [])
.controller('EfgController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular.module('Service1', [])
.service('XYZService', ['$http', function LmnoService($http) {
} ]);
This should easily be done, I would organize my application route configurations into the main app.js file.
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
}).when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
Then when you create a separate controller in each file just reference the application name as such:
myApp.controller('MonthlyCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.visible = true;
}])
You will also notice I am using the array initializer way, this will save you some hastles when you are doing minification.
You can follow this convention:
First load all the library dependencies like angular, angular-routes etc
then your config holder js file which contains your module declaration.
then all other files with controller methods.
I would map specific modules to functionality (and not by layers), each one containing its concerned controllers, services and appropriate directives.
You would have one module called 'places.list' for instance, containing all controllers, services/factories and directives associated to it.
The rule is: one module, one file, otherwise you would be forced to declare those files in order... (first modules declaration..then controllers etc.. ugly)
If you split your modules in the right way, you will notice that each one is easy to maintain and doesn't contain in general a huge amount of code.
Even each route declaration (.config) would be split across those modules.
=> All the route concerning places would be declared inside the module places.list.
Indeed, it would be ugly (and difficult to maintain) to declare the whole navigation rules in your main module..
Thus, each module would be easily testable by loading only specific module's dependencies that are relevant for the test.