Is there a way to provide a non-Angular injection target to the Angular $injector such that Angular constructs like $http, $scope, $location or $q can be injected into it?
//non-angular injection container
var injectionTarget= {
$http:undefined,
$scope:undefined
}
//means to inject into target - this is the part in question
var injector = angular.injector();
injector.injectInto( injectionTarget, ["$http", "$scope"]);
I'm having the hardest time finding any info on how to accomplish what I would assume is a very sought-after feature.
I think that probably the easiest way to do this would be to register your objects as services with the module.
var myObject = {} //Defined elsewhere or here as empty
app.service(‘aReferenceName’, function($http){
myObject.$http = $http;
return myObject;
});
This would have the double effect of setting the properties you want on your object, and making it accessible from angular as needed. It's also a pretty simple block of code. Note the implication though that as a service it would be a singleton from angular's perspective. If you need to do it as a class with many instances, you'll be wanting a factory.
Related
I have couple of question
Angular JS - Is it possible to create factory or service with $provider other than in config()
Is it possible to create $Provider instance with injector
i,e angular.injector(['ng']).get("$provider"); - When I tried it was throwing error
Thanks in advance
Updating the question for more clarity
Actually my question was- is it possible to create a factory/service by the injector $provider. I know the difference between factory , service and provider.And also I know we can create a factory inside config() like
app.config(function($provide){
$provide.factory('newFac',function(){
return {
newT : 'This is from confing()'
};
});
});
Just curious to know whether is it possible to create factory outside config() with $provide
Angular JS - Is it possible to create factory or service with $provider other than in config()
Not clear what you are asking, but let me explain a bit. Only providers can be injected into config phase, nothing else, but the difference between provider/service/factory is not so much big, so basically you could write any factory, and inject into config, but you must name it correctly, and provide a method called $get this way angular knows what object exposes your factory into config. Don't get wrong, only providers can be injected into config, but you could write your factory as it's a provider.
Here's example.
var app = angular.module('test', []);
//simple service
app.service('service1', function(){
//some stuff
});
//simple provider
app.provider('service2', function(){
//some stuff
this.$get = function(){ //now this provider can be injected into config
return this; //there are only 2 differences, `app.provider` and `this.$get` method
}
});
app.config(function(service2Provider){ //notice `Provider` prefix
//some stuff
});
angular.injector(['ng']).get("$provider");
This will work on your module, for example, your provider is TestProvider, and the module that creates this provider is TestModule, then you must use
angular.injector(['TestModule']).get("TestProvider");
Also it works for services, constants, values, factories
I was wondering when would you use $injector.get('someService') vs injecting that service directly.
Basically, what is the difference of the two code below?
angular
.module('myApp')
.controller('MyController', MyController);
/** Difference of this **/
MyController.$inject = ['$rootScope', '$route']; // and more services
MyController($rootScope, $route)
{
/* ... */
}
/** And this **/
MyController.$inject = ['$injector'];
MyController($injector)
{
var $rootScope = $injector.get('$rootScope');
var $route = $injector.get('$route');
/* and so on */
}
If you are not sure what your controller needs, or that you will update it in the future, and you have it refactored, then using $injector it is a lot easier to add/remove dependencies.
The first approach uses dependency injection while the second uses service locator pattern. Dependency injection has the following advantages:
It makes a component's dependencies explicit. For example, you can look at the constructor function of a controller and immediately know what dependencies it needs. With service locator, you usually have no idea because the controller might invoke the service locator any time.
It makes unit test easier. For example you can use the $controller service to instantiate a controller and feed it with mock objects via the locals argument. That's certainly easier than having to define a bunch of AngularJS services or factories so that $injector can resolve them. It becomes worse when coupled with #1: you might not be aware of all dependencies a component needs in order to supply all the necessary mocks.
Service locator does offer some flexibility thought. For example, your code might use a service only if it exists, like this:
if ($injector.has('serviceA')) $injector.get('serviceA').doSomething()
else someSomethingElse()
With dependency injection, if serviceA doesn't exist by the time your component is constructed, you'll get an error.
As to ease of refactoring, I don't think it's difficult to add/remove parameters to the constructor function. As pointed out in a comment to your question, tools like ngAnnotate will help make the declaration DRYer.
Therefore, I would just stick with dependency injection and only use service locator when absolutely necessary.
what I am trying to do here is to create a similar controller but not exactly the same, a few aspects of the controller need to be override. if it is the case in java then I probably just subclass and override the method I needed. but here in AngularJs not sure what is the best way to achieve this.
I really don't want to duplicate the code. refactoring the existing code is preferred but due to timeline issue have to delay that a bit.
so what is the correspondence of subclass and override in AngularJS
When you really need controller inheritance (including $scope), I think your question is best answered here: What's the recommended way to extend AngularJS controllers?
You can create a service and inherit its behaviour in any controller just by injecting it.
app.service("reusableCode", function() {
var reusableCode = {};
reusableCode.commonMethod = function() {
alert('Hello, World!');
};
return reusableCode;
});
Then in your controller that you want to extend from the above reusableCode service:
app.controller('MainCtrl', function($scope, reusableCode) {
angular.extend($scope, reusableCode);
// now you can access all the properties of reusableCode in this $scope
$scope.commonMethod()
});
DEMO PLUNKER: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview
So this is really weird, maybe it has a simple answer I'm missing. The following code gives an unknown provider error:
var foo = angular.module('foo', [ 'ngRoute', 'ngAnimate', 'ngCookies' ]);
foo.factory('fooApi', function ($scope, $http) {
var url = '/api/';
var factory = {};
factory.action = function (fields) {
fields.userid = $scope.userid;
fields.token = $scope.token;
console.log(JSON.stringify(fields));
return $http.post(url, { data: fields });
};
return factory;
})
.controller('loginController', function ($scope, fooApi) {
// do stuff
});
It's all loading together in the same file, and I'd think the factory being first would resolve and the injector would be able to find it when referenced below. But it gives an unknown provider error.
However, if I comment out the controller and wait for the page to load and then do the exact same controller declaration in the Chrome JS console it works fine.
Anyone run into this before and know how to deal with it? I haven't been able to find this same exact issue anywhere.
Like #tasseKATT said, you can not inject $scope into a service, particularly a factory. Maybe your confusion is because $scope can be injected in controllers, so you tried to injected into a factory.
An interesting thing is that the $scope that you see being injected into controllers is not a service - like the rest of the injectable stuff -, but is a Scope object.
The main purpose of $scope is a king of glue between views and controllers, it doesn't make much sense to pass a $scope into a service.
The services only have access to the $rootScope service.
If you need to pass the $scope of a specific controller to a service always you can pass it like parameter of a function in the service. This approach is not recommended because starting to break the SoC and the single responsibility principle, but maybe could fit you.
Good luck :-)
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) {
....
}]);