angular controller best practices - angularjs

Is it ok to do this :
.controller('ComputeCtrl', function($scope, $ionicPopup, $timeout, sessionService) {...})
or we should better write the dependencies like this :
.controller('ComputeCtrl', ['$scope', '$ionicPopup', '$timeout', 'sessionService', function($scope, $ionicPopup, $timeout, sessionService) {...}])
In the ionic starter app, the first version is used.
Thanks

The second syntax is used, when you want to minify your code. Because the variable names get renamed from $scope to something like a, angularjs won't be able to determine, which module you want to inject. Because of that, you provide a string that tells angular, which module do you want.
If you don't want to minify your code, you can safely use the first syntax.
If you don't like the second syntax, but you want to minify your code, you can use ngAnnotate https://github.com/olov/ng-annotate which will add the annotations for the second syntax on build time (maybe along with grunt-ng-annotate https://www.npmjs.org/package/grunt-ng-annotate).

Since the seed project is not using minification stuff, it is fine written in first way.
But if you are considering minification make sure you use second version.

Related

Error: [$injector:strictdi] is not using explicit annotation and cannot be invoked in strict mode

After upgrade to angular-ui-router 1.0.5 (from 1.0.0) I get the following:
Error: [$injector:strictdi] function(searchScope) is not using explicit annotation and cannot be invoked in strict mode
http://errors.angularjs.org/1.6.5/$injector/strictdi?p0=function(searchScope)
I understand from here that this means I
enabled strict mode, in order to detect where you forgot to use $inject or the array notation to make sure your code can be safely
minified.
How is this supposed to help me finding the Error? Is it in the Defintion searchScope or is the searchScope itself wrongly injected somewhere? How can I find where?
It's definition is
.factory('searchScope', ['$location', '$rootScope', 'Query', '$http', '$stateParams', 'language', '$state',
function($location, $rootScope, Query, $http, $stateParams, language, $state) {
(angular 1.6.5)
Any suggestions?
edit
SearchScope is a service I use in a lot of services and directives. I checked all components using it and all their definitions looked like
.directive('foodirective', ['searchScope', function (searchScope) {
which seem pretty correct to me...
Since original commenter never claimed his points here is the correct Answer by #Estus Flask:
This probably means that a function that injects searchScope is called in some route controller or resolver that weren't called before for some reason.
That helped me to find the source of the error very soon.

Angularjs $scope in controller

Just to mention that, I am a very much newbie in Angularjs.
While writing a controller I see I can use
controller('MyController', ['$scope', function($scope) {}])
or
controller('MyController', function($scope) {})
What is the difference between the both ?
Both are same.
When the javascript code is minified, all local variables in the function are changed to smaller variables to reduce size. for example.
function (largevariablename){
largevariablename = 123;
}
will be converted to
function (a){
a= 123;
}
But in case of angular if $scope is minified to s. Then the dependency injection fails searching for s. So angular injector will inject the the string value it finds in the array and inject it instead of the local varaiable in you define it in below way
controller('MyController', ['$scope', function($scope) {}])
So incase if your code in not going to be minified you can use the simple version
controller('MyController', function($scope) {})
This is mainly used for minification. When you minify the js
controller('MyController', function($scope) {})
will be converted to
controller('MyController', function(a) {})
and it will give the error for a is undefined. When you provide the dependency as
controller('MyController', ['$scope', function($scope) {}])
it will map a to $scope and it will work fine.
In fact, you should not use callbacks(Anonymous Functions) directly to define controllers.
You should use separate functions and $inject module to manually identify your dependencies.
controller('MyController', MyController);
MyController.$inject = ['$scope'];
function MyController($scope){
};
Why use named functions ?
This produces more readable code, is much easier to debug, and reduces
the amount of nested callback code.
Why use $inject ?
This technique mirrors the technique used by ng-annotate, which I
recommend for automating the creation of minification safe
dependencies. If ng-annotate detects injection has already been made,
it will not duplicate it.
Also, this safeguards your dependencies from being vulnerable to
minification issues when parameters may be mangled.
This is extracted from well-know John Papa Angularjs Style Guide

Angularjs dependency injection, array, $inject, factory

I'm kind of a big angularJS newbie and I'd like some highlights concerning dependency injection.
I've done some research and here is what I understand so far.
I have 2 service files (using factories) :
-mogService.js
angular.module('anglober.services').factory('mogService', ['$http', function($http) {
var mogService = {};
//mogService code here
return mogService;
}]);
-modalService.js
angular.module('anglober.services').factory('modalService', ['$modal',
function ($modal) {
//modalService code here
}]);
One controller file :
-mogCtrl.js
angular.module('anglober.controllers').controller('mogCtrl', ['$scope', 'mogService','modalService', function ($scope, mogService, modalService) {
//code using mogService and modalService parameters
}]);
As I understand it, the dependency injection is done by passing my services as parameters of the function parameter in my controller declaration, the array of string is here so that after minification, angular still knows which variable is what.
However, the modalService variable is undefined when I test my code. The mogService is recognized alright though (only if I remove any call to the modalService variable).
What am I doing wrong ?
I've read things about using $inject, which is the better practice and why ?
I'm declaring modules in the app.js as follows :
angular.module('anglober.services', ['ui.bootstrap']);
angular.module('anglober.controllers', []);
var app = angular.module('anglober', ['anglober.controllers', 'anglober.services', 'anglober.directives']);
Is this good practice ? Declaring modules and their respective dependencies in one file then only use "getters" without the dependencies array parameter in module files ?
Thanks for your time.
Three steps that work for me (plunkr):
1. Be sure you define a module's dependencies only once.
Indeed, check that angular.module('anglober.services', [...]); is indeed called only once with the second argument.
At the same time, be sure to call these lines before the actual services/controllers /... definitons.
2. Wire every dependency
You should add 'anglober.services' dependency to 'anglober.controllers': the last requires modalService which requires $modal, it may help angular anyway.
3. Add possible missing lib requirements, in the right order
First jQuery, then angular, bootstrap and eventually bootstrap.ui and your modules.

AngularJS - dependency injection

I would like to know if there is a difference between the two next lines and why to use one of those (the two work as expected)
phonecatApp.controller('PhoneListCtrl', function($scope, $http) {...});
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', function($scope, $http) {...}]);
I took it from the official AngularJS tutorial and I know there is an explanation about this modification but I don't understand it...
http://docs.angularjs.org/tutorial/step_05
Thanks in advance!
If you minify your first line you get:
phonecatApp.controller("PhoneListCtrl",function(e,t){})
The dependency injection won't work then, because Angular has no idea what e and t are. Compare that to minifying the second version:
phonecatApp.controller("PhoneListCtrl",["$scope","$http",function(e,t){}])
The function parameters are still renamed, but $scope and $http are given in the array so the injection can go ahead as expected.
There is no difference in terms of functionality. The first one may get messed up if your code is minified because angular resolves from the argument names. The latter has some kind of protection against minification because you are already passing dependencies in array.
AngularJS 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)
For more information, see AngularJS Developer Guide - Dependency Injection

minifying angular

I have this interest in automate/simplify angular project with a compiler tool, which might work on everything else, but angular inject and namespacing is awkward enough to escape compiler knowledge. What is the best/professional method for doing this?
thanks, just one last thing,
app.controller('ctrl',['$rootScope',function($rootScope){
...
}]);
works when minified, but how do I minify
app.config(['$routeProvider', function($routeProvider){
}]);
and does it work when I minify successive actions?
app.controller(...).directive(...).run(...)
In Angular, you need to annotate functions for the injector to know which dependencies you want to inject in your function. There are basically three ways to inject dependencies in your function which are being described on official angular website. The three ways are:
1.Use the inline array annotation
yourModule.controller('yourController', ['$scope', function($scope) {}]);
2.Use the $inject property annotation
var yourController = function($scope) {};
yourController.$inject = ['$scope'];
yourModule.controller('yourController', yourController);
3.Implictly from the function parameter names
yourModule.controller('yourController', function($scope) {});
Now when you minify your project, your dependencies names will get renamed.
In first case your code will be like
yourModule.controller('yourController', ['$scope', function(e) {}]);
In third case your code will be like
yourModule.controller('yourController', function(e) {});
It will break your app because angular has no way to recognize your dependency name. So it is advised never to use implicit dependency injection in your project. From the above two inline array annotation is the most popular way amongst programmers.
I would recommend using https://github.com/olov/ng-annotate. It will allow you to write your code like follows.
angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) {
});
and then ngAnnotate turns it into the following which is safe for minification.
angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) {
}]);
The minifyer leaves strings untouched, that's why we use the array notation.
Chaining methods wont change the way the minifyer keeps strings intact.
var app=module(myapp);
app.config(['$routeProvider', function($routeProvider){
$routeProvider.dosomestuffs()
}]);
will be minified in something like
var a=module(myapp);
a.config(['$routeProvider', function(b){
b.dosomestuffs()
}]);
but angular will still find its way around thanks to the '$routeProvider' string.
If you always use annotations there should not be problems minifying angular scripts.
app.controller(['$scope', function(mrScope) {
mrScope.yourProperty = 'it does not matter the dependency variable names if you use annotations';
}]);
As long as you use the array notation for the dependencies you are injecting, no minification trouble is expected. The minification tool you are using should handle any of your examples without trouble (on my project we're using uglify to accomplish that).
In fact, for oddly named injections (named with dots and chars that result in invalid function names like ABC.CDE), the array notation is the best way to inject them.
I had the same problem when minifying, and like you, it only failed for the $routeProvider config elements.. The answer for me was to use the $inject method like Himanshu says for my configs, even though the syntax you show for your first example works for my controllers. So I'll post my .config() code here since I don't see it specifically listed in the answers above:
var app = angular.module('myApp');
var RouteProviderConfig = function ($routeProvider) {
$routeProvider
.when(...)
.otherwise(...);
};
RouteProviderConfig.$inject = ['$routeProvider'];
app.config(RouteProviderConfig);
This fixed my error for configs, but my controllers work the way your first example is written:
app.controller('ctrl',['$rootScope',function($rootScope){
...
}]);
The Angular Documentation seems to suggest that both ways should work, so I think it's possible there is a bug with configs, or $routeProvider, or something else entirely...

Resources