At the bottom of Angular's Dependency Injection guide, I came across this snippet about factory methods, and I'm having a hard time understanding where the "depProvider" and "depService" are being defined:
Here's the snippet:
angular.module('myModule', []).
config(['depProvider', function(depProvider){
...
}]).
factory('serviceId', ['depService', function(depService) {
...
}]).
directive('directiveName', ['depService', function(depService) {
...
}]).
filter('filterName', ['depService', function(depService) {
...
}]).
run(['depService', function(depService) {
...
}]);
Am I correct in that 'depProvider' and 'depService' are injected into these definitions, and would have to be defined elsewhere? Or are these built-in dependencies?
I assume that those are fictional modules that should show you how a factory (or anything else) could *dep*end on another service.
They are no built-in modules or anything.
Yes, you're right. That dependencies can be in the same module or in any module declared as a dependency (modules can have dependencies too).
For what it's worth every Angular built-in service starts with a $ so they are easily spotted (e.g. $scope, $http, $timeout etc).
Related
Is it possible to use ng-annotate with angular module construction?
For example,
angular.module("testApp", ["ui.router"])
.controller("FrontController", function($scope, $window, $log) {
...
});
gets annotated with the various dependencies. However, I've not been able to get the same to work with module instanciation, because removing the ["ui.router"] array changes the module constructor to a module getter.
Furthermore, there is no documentation on this particular topic that I've been able to find.
I am not seeing the difference (or pros/cons) to directly injecting my dependencies vs. using $injector in angularjs. Why would I do one or the other? Here is a sample of the two.
angular
.module("myApp")
.factory("myService", [
"$injector", function($injector) {
var $http = $injector.get("$http");
//used $injector to get $http, now get some data, etc.
}
]);
angular
.module("myApp")
.factory("myService", [
"$http", function($http) {
//just use $http to get some data, etc.
}
]);
In most cases, the second method is simple, readable and it just works, especially if you use a precompiler that writes the string values for you, e.g. ng-annotate:
angular.module("myApp")
.factory("myService", /*#ngInject*/ function($http, $locale, $window) {
});
I see no reason why you should avoid it.
The first method, using $injector should be reserved for specific cases, e.g. when there are too many injections (and you don't want to refactor right now) or in some specific cases in tests.
It also makes unit testing much easier to use the first example. You simply mock the injections and your dependencies are handled for you. There are ways to get around it for the second example, but you're just making your life more difficult.
Both solutions work, but difference is in the code readability and extensibility.
Using the second solution you see all module's dependecies by factory definition.
angular
.module("myApp")
.factory("myService", [
"$myService", function($myService) {
}
]);
If you injecting a whole $injector, your dependencies will be fixed. You won't be able to inject other service with consistent API.
angular
.module("myApp")
.factory("myService", [
"$injector", function($injector) {
var $myService = $injector.get("myService");
}
]);
To sum up, both solutions work, but use the first posiblity.
I've got the guts of a routing architecture in Angular that will dynamically download and inject Angular Controllers and Services ... the Controller part works fine, and I'm trying to download dependant services via $route's .resolve property.
Now, say if I have a factory declared in scope while the page starts up, it registers fine and Controllers that use it resolve fine, e.g:
myModule.factory('MyInjectedDep', function() {
return {};
});
....
MyController = function(MyInjectedDep)
But if I try and register that dependency at "run time" (for want of a better phrase), I get a Circular Dependency error. e.g:
$route.routes[routeItem.route] = {
resolve: {
MyInjectedDep: ['$injector', function($injector) {
// In real code I download/eval this via $http but same behavior occurs
myModule.factory('MyInjectedDep', function() {
return {};
});
}]
}
}
So when my Controller is then initiated:
MyController = function(MyInjectedDep)
I get a circular dependency error, but no dependency trace in the error message?
Error: Circular dependency:
Any ideas appreciated
The key is latching onto $provide at configuration time. If you grab $provide at configuration time and maintain a reference to it, you can use it to register your factory like:
$provide.factory.apply(null, ['MyInjectedDep', [function() {
return {};
]}]);
I have a provider/service designed to do this, adapted from some other samples on github: https://github.com/afterglowtech/angular-couchPotato .
It's primarily designed to load from AMD, but you could probably use it's registerXXX functions with $http, or at least copy the relevant portions of its code. Don't let the size of the repository fool you -- the actual provider/service is about one page of code https://github.com/afterglowtech/angular-couchPotato/blob/master/src/couchPotato.js .
I'm aware that for the purposes of minification and obfuscation we should always use the $injector (by way of controllerName.$inject = ['$service', '$service2']) to specify the actual service names that are required.
If I write a custom service that relies on other services, however, can/should I do the same thing? The only examples I can find for using the .$inject method are called on Controllers.
If I am doing
myModule.factory('myService', function($rootScope, anotherService) {
return {
foo: 'bar'
});
Should I append this?
myService.$inject = ['$rootScope', 'anotherService'];
Or perhaps it's applied to the module as a whole then?
myModule.$inject = ['$rootScope', 'anotherService'];
...But maybe in that case, the module is already keeping track of its services, and hence minification is not a concern?
Check the dependency injection guide, section Inline Annotation.
The following is also a valid syntax, and it is safe for minification:
myModule.factory('myService', ['$rootScope', 'anotherService',
function($rootScope, anotherService) {
....
}]);
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...