What is the purpose of square bracket usage in Angular? - javascript

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.

Related

How to inject dependencies to a named angular directive controller?

If I have code similar to this question on injecting another controller to a directive:
angular.module('paramApp', []);
angular.module('paramApp').controller('ParamCtrl', ['$scope', '$http', function($scope, $http) {
.
.
.
}]);
angular.module('deviceApp', ['paramApp']);
angular.module('deviceApp').directive('DeviceDirective', function () {
return {
.
.
.
controller: 'ParamCtrl'
};
});
When I minify js, the injected dependencies of $scope and $http break, how can I explicitly define the dependencies of ParamCtrl when creating DeviceDirective to prevent uncaught injector issues with bundled js?
I am very late to this question but I'll give it a shot.
The syntax is based on John Papa's Angular style guide
First you need a way to make your controller reusable. Convert it from an anonymous function to a named function and pass it to your angular app like so:
// Named controller function
ParamCtrl function($scope, $http) {
this.doStuff
}
// Bind it to your app
angular.module('paramApp').controller('ParamCtrl', ['$scope', '$http', ParamCtrl );
// While we are at it, do the same with your directive
DeviceDirective function (controlerParam) {
return {
...
controller: controlerParam
}
}
// Bind it to your app
angular.module('deviceApp', ['ParamCtrl', DeviceDirective]);
However, if you meant to pass the controller's scope to your directive refer to fiznool's post

AngularJS: avoid using the 'angular' global object

I'm wondering if it would be a good practice to avoid using the 'angular' global object within Controllers, Services, etc.
For the sake of example, let's say we want to call the function:
angular.isDefined(myVar)
How shall we reference the 'angular' object?
Options:
1 Just use it, might get some 'variable is not defined' warning from IDEs
2 Reference the 'angular' dependency the AMD way
define([
'angular'
], function (angular) {
'use strict';
return ['$log', '$filter', function ($log, $filter) {
return {
// ... code ...
angular.isDefined(myVar);
};
}];
}
);
3 Reference 'angular' the Angular way
module.factory('ang', function() { return angular; });
define([], function () {
'use strict';
return ['ang', '$log', '$filter', function (ang, $log, $filter) {
return {
// ... code ...
ang.isDefined(myVar);
};
}];
}
);
I'd go for Option 3, just wondering what would be the best way.
The fact that you're using Angular in the first place means you don't have to worry about it being undefined. From there it's really just a static analysis problem... you want to make it clear that you're referencing a global variable.
Both solutions you've presented are good. There are a couple of other options too:
Inject $window and reference $window.angular explicitly
Configure your static analysis tool so it knows angular is defined elsewhere. You can do so with the globals property in .jshintrc.
I would probably go for a combination of $window and your option 3, to prevent warnings when analysing that file:
module.factory('ang', function($window) { return $window.angular; });

In angular modules why the body is the last array element?

This is more of an architectural question.
One of the most common forms of defining an angular module is this:
angular.module('app', [])
.controller('Ctrl', ['$scope', function Ctrl($scope) {
//body...
}]);
But I don't find the syntax very intuitive. How about having the list of dependencies in an array like AMD:
angular.module('app', [])
.controller('Ctrl', ['$scope'],
function Ctrl($scope) {
//body...
});
This way the whole array will contain only string elements each of which refers to a module. The array matches the function parameters one by one. (kinda like arguments).
So my question is why Angular designers went for this convention?
It kind of does that in a sense. you can do this by using $inject.
function SomeCtrl ($scope) {
// do something with $scope
}
SomeCtrl.$inject = ['$scope'];
angular
.module('app', [])
.controller('SomeCtrl', SomeCtrl);
I am not a no Expert on this, but I did found a great post on how this process works and it might help answer your question: http://toddmotto.com/angular-js-dependency-injection-annotation-process/

AngularJS : get translate key in app.js

In my application, I use the AngularJS module Pascal Precht (translate module). I come to you because I can not get in my method myApp.Run of app.js a translation key.
I can do in a controller or a view. But impossible to get it at the initialization of the project. It shows me the key, not correspondence.
Do you have a solution?
Here is my code:
var myApp = angular.module('myApp', ['ngRoute', 'ngAnimate', 'myApp.filters', 'myApp.services', 'myApp.directives', 'pascalprecht.translate']);
// Declare routeProvider
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {templateUrl:'partials/connectView.html', controller:'ConnectController'});
$routeProvider.when('/homeView', {templateUrl:'partials/homeView.html', controller:'HomeController'});
}]);
// Declare translateProvider
myApp.config(['$translateProvider', function($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'res/localization/lang-',
suffix: '.json'
});
$translateProvider.preferredLanguage('fr_FR');
//$translateProvider.preferredLanguage('en_US');
}]);
// Declare Global variables
myApp.run(['$rootScope', '$filter', function($rootScope, $filter) {
$rootScope.list = false;
etc....
//I'm trying to get translate Key but it doesn't work
console.log($filter('translate')('MY_KEY'));
}]);
My AngularJS version is 1.2.16 (last stable version). Thx
Try injecting the $translate service in app.run().
angular-translate version 1.1.1 and below
myApp.run(['$rootScope', '$translate', '$log', function ($rootScope, $translate, $log) {
$log.debug($translate('MY_KEY'));
}]);
I'd also suggest you to upgrade to the latest version of Pascal Precht's angular-translate. There are some changes in the new version.
angular-translate version 2.0.0 and above
myApp.run(['$rootScope', '$translate', '$log', function ($rootScope, $translate, $log) {
// translate via promises (recommended way)
$translate(['MY_KEY', 'MY_OTHER_KEY'])
.then(function (translation) {
$log.debug(translation.MY_KEY);
});
// translate instantly from the internal state of loaded translation
$log.debug($translate.instant('MY_KEY'));
}]);
See this helpful migration guide.
Why don't you inject the $translate service in the run section
and then call it instead of using the filter?!
console.log($translate('MY_KEY'));
Well, Apparently I can't comment because of reputation issue, we came across something that might be what you are experiencing - since the locale file is downloaded only in the config part of angular, it might not be available (yet) when you call the translate.
We solved this by adding all the locale files upfront (we don't have many and they are small) and in the initialization we just choose the correct one, that way we avoid the problem.
(again this should probably be more of a comment then an answer, but I can't comment...)
This is not a solution for your issue, but if you try the following code in your 'run', you will get an idea, why the translation is not available at the initializing state.
myApp.run(['$rootScope', '$filter','$timeout', function($rootScope, $filter,$timeout) {
$timeout(function(){
alert($filter('translate')('MY_KEY'));
},5000)
}]);
Problem here is, by the time the translation is being loaded the 'run' will be executed. So it cannot be assured that you will get the translation loaded at that time.

do I have to declare the $http service when requiring other services?

angular.module("ABC.services").service("configService", [
'loggerService', function(logger, $http) {
debugger;
return this.get = function(onError, onSuccess) {
return $http.get("/api/config/").success(function(config) {
logger.debug('loaded config');
return onSuccess(config);
}).error(onError);
};
}
]);
(I have a logger that's more complex than $log)
I find that at the debugger line $http is undefined unless I include '$http' in the list of dependencies. The docs don't discuss this use case. Their example of native service injection looks like:
angular.module('myModule', [], function($provide) {
Would I be required to declare $provide as a dependency if I was also using one of my own services? I'm just really confused about when I can rely on the automatic injection of $ services and when I have to explicitly declare them.
You should only inject the modules when you use it. If you don't use it in the code, you don't have to inject. (In the example you referred, because the code uses the $provide, that is why it is injected.)
When you use array notation to inject the modules, you need the modules declared in the array to match the parameters in the function. For example:
angular.module("ABC.services").service("configService", [
'loggerService', '$http', function(logger, $http) {
or without using array notation
angular.module("ABC.services").service("configService", function(loggerService, $http) { ...
And the advantage of using array notation is it protects against minification.

Categories