How to get a service from angular on initialization? - angularjs

I have been looking for a way to get services on initialization of my angular-js application, but could not find how to get it to work. In my case I want to get the $location service to observe the url.
Looking around, I found the services can be retrieved from the injector. To get the injector, I bootstrapped my application like this:
var angularApp = angular.module("MyApp", []);
var angularInjector = angular.injector(["MyApp", "ng"]);
angularApp.run(initializeAngularApp);
initializeAngularApp()
{
var location = angularInjector.get("$location");
}
This throws an Error:
Unknown provider: $rootElementProvider <- $rootElement <- $location
My understanding is that initializeAngularApp() should get called once the injector is done initializing. But judging from the error I get, it would not be the case.
What is the best way to get the services from the injector when my application initializes?

I found my answer and I did not need to instantiate the injector myself to get the service.
Services are injectable in the run() function, so doing:
angularApp.run(intializeAngularApp);
with
initializeAngularApp($rootScope, $location)
{
$rootScope.location = $location;
$rootScope.$watch("location.url()", function () { alert("url changed"); });
}
works.

Related

Why can't I get an $location from the $injector?

I am new to angular and trying to use the $location service outside of the scope using $injector.
When I run the following code I keep getting an error
angular.injector(['myApp']).get('$location');
Any suggestions?
The $location service is not part of the myApp module, it is part of the internal ng module.
To get the $http service, use:
var $http = angular.injector(['ng']).get("$http");
The $location service depends on the $rootElement service which is created at bootstrap time. To get the $location service use:
var $rootElementModule = function($provide) {
$provide.value('$rootElement', angular.element(document))
}
var $location = angular.injector(['ng',$rootElementModule]).get("$location");

Controller testing fails due to the service dependency injection

When making use of a service in a controller test do you need to initialize the service in the same way you would the controller? By this I mean do you need to pass it its own dependencies?
For example I can initialize my controller like so:
// Instantiate the controller
searchController = $controller( 'VisibilitySearchController',{
$scope: scope,
dataService: dataService
});
}));
so do I need to initialize the service according to the components it needs like $http, $resource etc as well as make spyOn calls on its functions? Or is this/should this be sufficient? (Note - my tests fail when I do the following )
// Instantiate the dataService
dataService = $injector.get( 'dataService' );
it throws this error:
* Error: [$injector:unpr] Unknown provider: $resourceProvider <- $resource <- dataService
The relevant part of the service:
myAppServices.factory('dataService', ['$http', '$resource', 'authService', 'messageService', function ($http, $resource, authService, messageService) {
}
Side note
Note - we are using Maven as our build tool and only make use of Jasmine at this point - trying to bring Karma in as our test-runner as a Maven plugin.
You must provide all the dependencies but you can mock them. This can be done by jasmine like this for example:
var mockedDataService = jasmine.createSpyObj('dataService', ['getData', 'getOtherData']);
And then you inject this mocked service to $provider:
beforeEach(function () {
module(function ($provide) {
$provide.value('dataService', mockedDataService );
});
}
Instance of this mocked service can be retrieved like this then:
inject(function (dataService) {
var dataServiceInstance = dataService;
});
This will provider mocked dataService anytime it is needed. However if you need fully functional dataService you must instantiate it but always you can mock any of its dependecies.
While you can inject dependencies into the controller manually you don't need to do it as long as you have loaded the module the service belongs to.
In your case it looks like you have not loaded the ngResource module.
If you add beforeEach(module('ngResource')) to your test (and make sure the actual script file it lives in is included in Jasmine's fileset) you should not need to inject it manually.
Note that you do not need to load angular core services like $http, but since $resource is not part of core it needs to be loaded like this.
Injecting dependencies manually is mostly useful if you want to provide a mock implementation.

unknown provider, service isn't being injected

I'm confused about how to create services and inject (use) them in my controllers. It seems it is very simple but I can't get my code to work. I'm stuck with this error:
Error: [$injector:unpr] Unknown provider: Flickr
I define the service:
angular.module('myApp.services', [])
.provider('Flickr', function(){
// service code
})
Include it in my app module:
var app = angular.module('myApp', [
'ngResource',
'ngRoute',
'myApp.services'
]);
and then reference it in a controller:
app.controller('FlickrCtrl', ['$scope', '$routeParams', 'Flickr', function($scope, $routeParams, Flickr){
// controller stuff
});
and reference the files at the bottom of index.html
<script src='js/app.js'></script>
<script src='js/config.js'></script>
<script src='js/services/Flickr.js'></script>
<script src='js/controllers/flickr.js'></script>
Why can't angular find the service I defined when I ask it to inject it into the controller?
When using .provider, you are creating a provider that should return a configurable singleton. In many cases, this singleton is a singleton factory, spitting back an object that has services you can use.
First, you would need to refer to it as FlickrProvider instead of Flickr when you call it to set a config.
Without seeing more of your code, I can't tell if you're returning a new Flickr from your provider, which is what you would need to do in order to use a service instance in the way I think you're trying to do.
check out: http://docs.angularjs.org/guide/providers
Basically though, in order to inject and use Flickr like you are trying to do, you would need to do something like this:
.provider('Flickr',function FlickrProvider(){
this.$get = function(){
return new Flickr()
}
})
function Flickr(){
this.doSomething: function(){
//do something or return something
}
}
If you only want to define a service, and not make it a configurable provider, then use .factory instead, which will only need Flickr to be injected in order to be used.

AngularJS Logging to Server

New to AngularJS but slowly getting going with it and so far I like it. I'm coming from the Java/JSP world so bit of a learning curve!
Anyway, I'm trying to figure out how to send all logging to a server side service.
In my app module config I've overridden the Log implementation and I have this working fine - I have log statements automatically creating simply alerts.
Next step was to send them to the server. I've create a service for this using $resource. I then try to autowire the service into my app module config and this is where I've problems.
It's giving me a circular dependency error which I'm not sure what it means or how to resolve it.
Anyone done anything similar before who may have encountered this problem?
The error I'm seeing is 'Uncaught Error: Circular dependency: $browser <- $httpBackend <- $http <- $resource <- LoggingService <- $log <- $exceptionHandler <- $rootScope'
My app config is:
app.config(['$provide', function($provide) {
$provide.decorator('$log', function($delegate, LoggingService) {
var _info = $delegate.info;
var _error = $delegate.error;
$delegate.info = function(msg){
_info(msg);
};
$delegate.error = function(msg){
_error(msg);
//log.error(msg);
alert('Error:' + msg);
};
return $delegate;
});
}]);
Just trying to pass in my LoggingService results in the error.
My logging service is very simple:
app.factory('LoggingService', ['$resource', function($resource) {
return $resource('http://localhost:port/myservice/logging/', {port: ':8080'},
{
save: {method: 'POST'}
}
);
}]);
Regards,
Kevin.
As per Injecting Dependencies in config() modules - AngularJS
You can only use providers in .config
See registering a service with $provide
https://docs.angularjs.org/guide/services

How to inject dependencies into a provider using Angularjs?

Is it possible to do DI in a provider method?
In this example
angular.module('greet',[])
.provider('greeter',function() {
this.$get=function() {
};
})
.service('greeterService',function($http){
console.log($http);
})
;
Injecting $http into service appears to be the correct implementation, but it doesn't work in a provider method and it throws an error:
Unknown provider: $http
Does the provider method work with DI to inject services?
You can certainly inject $http to provider. Just make sure it appears in $get, not the function constructor. As follows:
angular.module('greet',[]).provider('greeter',function() {
this.$get = function($http) {
};
});
You can inject constants and other providers into a provider. Not services or factories - with one exception. It seems that you can inject the $injector service into a provider - at least, you can in AngularJS 1.3.16.
.provider('foo', ['$injector', function ($injector) {
var messagePrefix = $injector.get('msgPrefix');
this.message = '';
this.$get = function() {
var that = this;
return function() {
return messagePrefix + that.message;
}
};
}])
You can use the injector outside the $get method, but you still can't get services from it at configure time.
See here for a demo.
Following up on IgrCndd's answer, here's a pattern that might avoid potential nastiness:
angular.module('greet',[]).provider('greeter', function() {
var $http;
function logIt() {
console.log($http);
}
this.$get = ['$http', function(_$http_) {
$http = _$http_;
return {
logIt: logIt
};
}];
});
Note how similar this is to the equivalent service, making conversion between the two less troublesome:
angular.module('greet',[]).factory('greeter', ['$http', function($http) {
function logIt() {
console.log($http);
}
return {
logIt: logIt
};
});
You actually have to inject the dependency on $get and then store it to use on what you retrieve from $get. Not beautiful at all...
No, you can not inject a service into the provider itself.
Injecting a service into a provider's $get method is the same as injecting a service into a factory, but you can not inject it into the provider function directly.
The difference between $get and the provider itself is that the provider runs during the module loading phase whereas the $get is run when instantiating the service you are providing.
This implies that you can not use any service at all during the module loading/configuration phase of your modules. That is all the stuff you run inside your config blocks, such as when defining your app routes or states, can not make use of any service.
The only other thing you can inject into config blocks besides providers are constants.
You could do something like IgrCndd suggested. But if you needed to consume the provider in a config block, which is the provider's purpose after all, you will not have your values injected until much after. So it's not going to work unless you do some nasty hack using promises.
Further reading on injectables

Resources