Angular dependency injection in $resource factory - javascript

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(...)
}]);

Related

Angular Dependency Injection Annotation [duplicate]

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

Dynamically injecting a specific factory based on route parameter fails for some reason

I want to dynamically inject a factory into my Angular controller based on the route parameter. This is my route configuration:
$routeProvider
.when("/tables/:table",
{
controller: "tableController",
templateUrl: "/app/views/table.html",
resolve: {
"factory": function r($route) {
return $injector.get($route.current.params.table + "Factory"); // Error!
}
}
})
For instance, when the route is tables/employee, I want an employeeFactory to be injected into tableController, and so on.
Unfortunately, this configuration does not work — I am getting an Unknown provider: employeeFactory error in the r function.
On the other hand, I can instead pass an $injector service directly to the tableController and successfully resolve employeeFactory there:
(function (angular) {
var tableController = function ($routeParams, $injector) {
// employeeFactory resolves successfully here!
var factory = $injector.get($routeParams.table + "Factory");
};
angular.module("appModule").controller("tableController", tableController);
})(angular);
However, I do not like this approach because it follows the service locator anti-pattern. I would really like factory to be injected using routing configuration and not this ugly workaround.
So, why Angular is throwing an error when using $injector.get() with resolve, but successfully resolves the factory inside of the tableController? I am using Angular 1.4.4.
You apparently use $injector that was injected into config block, and it differs from $injector that is injected anywhere else. The former acts on service providers, the latter acts on service instances.
It should be
"factory": function r($route, $injector) {
return $injector.get($route.current.params.table + "Factory");
}

Resolving dependency for $state.go in Angular UI Router

I have a provider config which uses $state.go to jump to states. When I use $state as a function parameter it works, but when I try to modify the function parameters to support minification such as
.provider('Navigation',["$stateProvider","$state",function($stateProvider,$state)
then I get the following resolve dependency error :
Uncaught Error: [$injector:modulerr] Failed to instantiate module apfPrototypeJs due to:
Error: [$injector:unpr] Unknown provider: $state
How to circumvent this problem?
You can only inject providers in a provider because no services are instantiated at that point yet and also because the provider methods are used especially for configuration, they can only be accessed during the config phase of the app, it does not make sense to have the ability to inject any services. But You can inject any service (not provider) in the provider's constructor function defined via $get property.
i.e
.provider('Navigation',["$stateProvider",function($stateProvider) { //Inject provider here
this.$get = ["$state", function($state){ //Inject $state here
console.log($state)
}]
}]);
As an alternate syntax (using $inject to support minification) you could do:-
.provider('Navigation', function(){
this.$get = navigationService;
navigationService.$inject = ['$state'];
function navigationService($state) {
console.log($state)
}
}]);

Why is it necessary to specify Angular modules with factory functions, instead of similar how I specify Node modules?

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.

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