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() { }
Related
John Papa's famous Angular 1 Style Guide says to use IIFEs, to avoid the likes of var myApp = angular.module('myApp',[]);
and polluting the global namespace.
The example given is:
logger.js
(function() {
'use strict';
angular
.module('app')
.factory('logger', logger);
function logger() { }
})();
storage.js
(function() {
'use strict';
angular
.module('app')
.factory('storage', storage);
function storage() { }
})();
How does this work? Do I not need to declare the module at least once? E.g with angular.module('app',[]); (wrapped in an IIFE? (instead of var app = angular.module('app',[]); to avoid a global variable))?
However, the two usages of angular.module('app') in the example , do not declare, but will then evaluate angular.module('app') twice, which surely cannot be A Good Thing (in fact, I read a highly upvoted S.O. question earlier which said that this is A Bad Thing, and that there should be a single reference - but that would be a global, which is also A Bad Thing).
Which is it to be? Or can I declare my angular.module('app'), plus a few controllers, serves, factories, directives, in separate files, in separate IIFEs? If so, how?
Do I not need to declare the module at least once?
Yes, the module needs to be created with dependencies first:
app.js
angular.module('app',['ngRoute']);
This must be done before any subsequent scripts add to it. It need not be enclosed in an IIFE because it doesn't declare any global variables.
To avoid pollution of the global namespace, the following needs to be enclosed in an IIFE:
logger.js
(function() {
'use strict';
angular
.module('app')
.factory('logger', logger);
function logger() { }
})();
On the other hand
storage.js
'use strict';
angular.module('app')
.service('storage', function storage() {
var value;
this.get = function() { return value; };
this.set = function(val) {
value = val;
};
})
This does not need an IIFE because it does not declare any global variables or functions.
Both examples avoid polluting the global namespace.
For more information, see
AngularJS angular.module Method API Reference
angular.module('app') without array as 2nd param is a getter, it is ok to use it multiple times.
To improve this you need some build tool, e.g. we use webpack (it will wrap everything into IIFE for u btw) and now it looks like this:
logger.js :
export default ['$log', function($log) {}]
storage.js :
export default ['$http', function($http) {}]
module.js :
import logger from './logger.js';
import storage from './storage.js';
angular
.module('app')
.factory('logger', logger)
.factory('storage', storage);
I am new to angularJs. I am confused which one is best way to create a controller for ng-app="mainApp". In programming other programming languages that I had worked on suggest to keep relative data together. But in angularJs it's considered best practice to have new module for controllers when we can define controller over main app module. If we create controller on mainApp it will keep controller and bind which is what we want in other languages.
var myapp = angular.module("mainApp", []);
myapp.controller("testController", function($scope)
{
$scope.value = "test";
})
//OR
angular.module("mainApp", ["moduleController"]);
angular.module("moduleController", []).controller("testController", function($scope){
$scope.value = "test";
})
For production environment which one should be used.??
Option 1:
var myapp = angular.module("mainApp",[]);
myapp.controller("testController",function($scope)
{
$scope.value="test";
})
Option 2:
angular.module("mainApp", ["moduleController"]);
angular.module("moduleController",[]).controller("testController",function($scope){
$scope.value="test";
})
Option 2 is better because this will allow you to write your controllers in separate files. As a result your code will be more readable. It'll also help you if you want to reuse your controllers in other AngularJS projects.
For example, you can write the following code in one file e.g app.js:
angular.module('mainApp',['ngRoute', 'appController']);
And you can write the controller in another file e.g controllers.js:
var appController= angular.module('appController', []);
appController.controller('testController', ['$scope',
function($scope) {
$scope.value="test";
}
]);
Now, you can reuse the controllers in another project by just adding the controllers.js file in the project and adding dependency to appController in the app.js file.
Neither, none of them will run in production environment where all script will be minified. Angular's injector subsystem is able to find and resolve $scope, $location, $etc and provide them to the component as requested.
myapp.controller("testController",function($scope)
{
$scope.value="test";
})
However, upon minification, the code above will end up looking something like:
a.controller("testController",function(b)
{
b.value="test";
})
which would cause the dependency injection to fail and result in a runtime error.
You will have to use it as below:
var myapp=angular.module("mainApp",[]);
myapp.controller("testController", ['$scope',function($scope)
{
$scope.value="test";
}]);
In this case, the controller function is initialized inside of an array after a list of each dependency as string literals. This ensure dependency injection is properly maintained, even through minification or uglification.
What is the difference between:
myApp.controller('GreetingController', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
And
myApp.controller('GreetingController', function($scope) {
$scope.greeting = 'Hola!';
});
Some of the examples add dependency for scope and some are not.
Thank you.
Both your samples have a scope dependency injected, the difference being the first one uses the array notation which allows you to create custom naming, won't break when minifying, etc... It's the recommended way. For example, this works fine:
myApp.controller('GreetingController', ['$scope', function(myScopeAlias) {
myScopeAlias.greeting = 'Hola!';
}]);
See it in action here:
angular.module('test', [])
.controller('GreetingController', ['$scope', function(myScopeAlias) {
myScopeAlias.greeting = 'Hola!';
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="GreetingController">{{greeting}}</div>
Dependency Annotation
Angular invokes certain functions (like service factories and controllers) via the injector. You need to annotate these functions so that the injector knows what services to inject into the function. There are three ways of annotating your code with service name information:
Using the inline array annotation (preferred)
Using the $inject property annotation
Implicitly from the function parameter names (has caveats)
Read more: https://docs.angularjs.org/guide/di#dependency-annotation
I just want to emphasis one point:
Always using the inline array annotation if you project need to do the Uglification(Most real project need a uglification).
Building minification-safe Angular.js applications
I'm following the AngularJS tutorial on its site, and at the moment my controller is as followed, and the page loads perfectly:
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
});
As I was doing the "A Note on Minification" part of Step 5, I came up with this:
var phonecatApp = angular.module('phonecatApp', []);
function PhoneListCtrl($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}
//phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);
The page loads just fine with the second code, where I commented the creation of controller from the module. My question is, what's the difference between declaring a controller from the module and defining a function
There is no difference in the actual execution/behaviour, the second one is using something called a 'function constructor'. It is a common way of creating a class-like structure in javascript.
The first one under the hood will be doing the same thing it's just that angularJs' dependency injection model works with strings so this is easier to read.
Declaring a global function, AFAIK was possible in order to simplify simple demos and get up to speed quickly. In latest versions of Angular, it's not supported by default anymore (see https://github.com/angular/angular.js/commit/3f2232b5a181512fac23775b1df4a6ebda67d018).
It's bad practice anyway to pollute the global namespace with lots of functions.
Check out Todd Motto's style guide for angular: http://toddmotto.com/opinionated-angular-js-styleguide-for-teams/
Defining your controllers as a function expressions is important to keep your code DRY and allow for named stack traces:
This is okay:
function PhoneListCtrl($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}
The issue is that this puts the function in the global scope; ohhh nooo. The solution is to wrap the declaration in an IIFE. This allows you to keep the best practice of defining your functions and to not pollute the global scope.
Here's an example:
(function () {
angular.module('app', []);
// MainCtrl.js
function MainCtrl () {
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
// AnotherCtrl.js
function AnotherCtrl () {
}
angular
.module('app')
.controller('AnotherCtrl', AnotherCtrl);
// and so on...
})();
Like this:
function PhoneListCtrl($scope, $http) {
your function would be declared in the global scope.
And we know that is clearly bad to pollute the global scope since it may lead to variable/function conflict.
By wrapping it inside a controller, you isolate the scope.
In some AngularJS tutorials, angular app is defined as:
myApp = angular.module("myApp",[]);
But we can also do without it. The only difference I can see is when we define controller, we can't use idiom:
myApp.controller("myCtrl",function(){ })
but has to use
function myCtrl (){}
Is there any other benefits of defining myApp explicitly, given that I will only create a single app for my site? If I don't define myApp, then where my modules are attached to?
If there is, how I can recreate myApp in testing with Jasmin?
You can define controllers in (at least) 3 ways:
Define the controller as a global var (stored on the window object)
function Ctrl() {}
which is the same as doing:
window.Ctrl = function () {}
Create a module and use the returned instance to create new controllers:
var app = angular.module('app', []);
app.controller('Ctrl', function() {});
Create the controllers directly on the module without storing any references (the same as 2 but without using vars):
angular.module('app', []);
angular.module('app').controller('Ctrl', function() {});
From Angular's point of view, they all do the same, you can even mix them together and they will work. The only difference is that option 1 uses global vars while in options 2 and 3 the controllers are stored inside an Angular's private object.
I understand where you're coming from since the explanation for bootstrapping your Angular is all over the place. Having been playing with Angular only for a month (I'll share what I know anyways), I've seen how you have it defined above. I was also in the same scenario where I only have to define myApp once and not have multiple ones.
As an alternative, you can do something like this below. You'll notice that the Angular app and the controller doesn't have to live by the same namespace. I think that is more for readability and organization than anything.
JS:
window.app = {};
/** Bootstrap on document load and define the document along with
optional modules as I have below.
*/
angular.element(document).ready(function () {
app.ang = angular.bootstrap(document, ['ngResource', 'ngSanitize']);
// OR simply, works similarly.
// angular.bootstrap(document, []);
});
/** Define Angular Controller */
app.myController= function ($scope, $resource, $timeout) {
};
HTML:
<div role="main" ng-controller="app.myController"></div>
you have to define the app with angular.module anyway. myApp.controller and function myCtrl are the same..