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.
Related
A doubt about DI on factory resource:
AFAIK the below sample is the recommended way to inject dependencies in Angular1:
angular.module('myApp').factory('Resource', Resource);
Resource.$inject = ['$resource', 'CONSTANTS'];
function Resource($resource, CONSTANTS) {
return $resource(CONSTANTS.server_url + '/resource/:id');
}
But I'm having problems in use it combined with the new keyword in my Controller:
var resource = new Resource();
This results in an error saying that CONSTANTS is undefined. Using the below syntax, it works normally.
angular.module('myApp').factory('Resource', ['$resource', 'CONSTANTS', function($resource, CONSTANTS) {
return $resource(CONSTANTS.server_url + '/resource/:id');
}]);
Why this happens?
Angular's injections system only works if you're using Angular's injection system. By doing new Resource(), you're instantiating a plain Javascript "class" yourself using plain Javascript; the $inject property on Resource doesn't magically do anything in this case.
What you're doing with module(..).factory('Resource', ..) is to define 'Resource' as an injectable dependency. You must now use this as a dependency for your controller, and Angular will work its injection system:
module(..).controller('MyController', ['Resource', function (Resource) {
Resource.get(...)
}]);
I'm reading http://www.alexrothenberg.com/2013/02/11/the-magic-behind-angularjs-dependency-injection.html and
it turned out that angularjs dependency injection has problems if you minify your javascript
so I'm wondering if instead of
var MyController = function($scope, $http) {
$http.get('https://api.github.com/repos/angular/angular.js/commits')
.then(function(response) {
$scope.commits = response.data
})
}
you should use
var MyController = ['$scope', '$http', function($scope, $http) {
$http.get('https://api.github.com/repos/angular/angular.js/commits')
.then(function(response) {
$scope.commits = response.data
})
}]
all in all I thought the second snippet was for the old version of angularjs but ....
Should I always use the inject way (the second one) ?
Yes, always! So this way even if your minifer converts $scope to variable a and $http to variable b, their identity is still preserved in the strings.
See this page of AngularJS docs, scroll down to A Note on Minification.
UPDATE
Alternatively, you can use ng-annotate npm package in your build process to avoid this verbosity.
It is safer to use the second variant but it is also possible to use the first variant safely with ngmin.
UPDATE:
Now ng-annotate becomes a new default tool to solve this issue.
Yes, you need to use explicit dependency injection (second variant). But since Angular 1.3.1 you can turn off implicit dependency injection, it's really helpful to solve potential problems with renaming at once (before minification).
Turning off implicit DI, using strictDi config property:
angular.bootstrap(document, ['myApp'], {
strictDi: true
});
Turning off implicit DI, using ng-strict-di directive:
<html ng-app="myApp" ng-strict-di>
Just to point out that if you use
Yeoman
there is no need to do like
var MyController = ['$scope', '$http', function($scope, $http) {
$http.get('https://api.github.com/repos/angular/angular.js/commits')
.then(function(response) {
$scope.commits = response.data
})
}]
because grunt during minify take into account how to manage DI.
Like OZ_ said, Use ngmin to minify all angular js file, like directive.js service.js. After that you can use Closure compiler to optimize it.
ref:
How to minify angularjs scripts
Build with YO
You might want to use $inject as it mentioned here:
MyController.$inject = ['$scope', '$http'];
function MyController($scope, $http) {
$http.get('https://api.github.com/repos/angular/angular.js/commits')
.then(function(response) {
$scope.commits = response.data
})
}
Use Strict Dependency Injection to Diagnose Problems
With Implicit Annotation, code will break when minified.
From the Docs:
Implicit Annotation
Careful: If you plan to minify your code, your service names will get renamed and break your app.
You can add an ng-strict-di directive on the same element as ng-app to opt into strict DI mode.
<body ng-app="myApp" ng-strict-di>
Strict mode throws an error whenever a service tries to use implicit annotations.
This can be useful to determining finding problems.
For more information, see
AngularJS Developer Guide - Using Strict Dependency Injection
AngularJS ng-app Directive API Reference
AngularJS Error Reference - Error: $injector:unpr Unknown Provider
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; });
I've recently begun using angularjs. But it's concept of modules confuses me.
In one of the angular tutorials, there's the following code:
'use strict';
/* Services */
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
//this line's added by me
phonecatServices.constant('SomeConstant', 123);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
Why does angularjs require helper functions like constant or factory, when it can just as well define modules in a manner similar to that of nodejs which is much cleaner? I'm confused as to what advantages this approach has.
var $resource = require('$resource');
var SomeConstant = 123;
var Phone = $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
};
exports.SomeConstant = SomeConstant;
exports.Phone = Phone;
The answer seems centered around angular's dependency injection.
Consider angular.module to be as the api says, a global means to create/register or retrieve your module. A module needs to be created in this way so the $injector, which is a function that takes a list of module names that have been registered, can find it at the time of bootstrapping.
I wouldn't consider the factory function a 'helper', but instead actually a way of specifying to angular js's dependency injection how a service is supposed to be created. Or as the dependency injection guide puts it -- we are 'teaching' the $injector how to create a service:
// Provide the wiring information in a module
angular.module('myModule', []).
// Teach the injector how to build a 'greeter'
// Notice that greeter itself is dependent on '$window'
factory('greeter', function($window) {
// This is a factory function, and is responsible for
// creating the 'greet' service.
return {
greet: function(text) {
$window.alert(text);
}
};
});
// New injector is created from the module.
// (This is usually done automatically by angular bootstrap)
var injector = angular.injector(['myModule', 'ng']);
// Request any dependency from the injector
var greeter = injector.get('greeter');
The guide also reminds us that here, the injector is created directly from a module, but usually angular's bootstrapper takes care of that for us.
So, in short, angular.module tells angular how to resolve modules (which it does via $injector), and factory tells angular how to make or them when they're needed. In contrast, Node's modules have a one-to-one mapping with files and are resolved and made in this way.
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.