I have a service like:
angular.module('app').factory('ExampleService', function(){
this.f1 = function(world){
return 'Hello '+world;
}
return this;
})
I would like to test it from the JavaScript console and call the function f1() of the service.
How can I do that?
TLDR: In one line the command you are looking for:
angular.element(document.body).injector().get('serviceName')
Deep dive
AngularJS uses Dependency Injection (DI) to inject services/factories into your components,directives and other services. So what you need to do to get a service is to get the injector of AngularJS first (the injector is responsible for wiring up all the dependencies and providing them to components).
To get the injector of your app you need to grab it from an element that angular is handling. For example if your app is registered on the body element you call injector = angular.element(document.body).injector()
From the retrieved injector you can then get whatever service you like with injector.get('ServiceName')
More information on that in this answer: Can't retrieve the injector from angular
And even more here: Call AngularJS from legacy code
Another useful trick to get the $scope of a particular element.
Select the element with the DOM inspection tool of your developer tools and then run the following line ($0 is always the selected element):
angular.element($0).scope()
First of all, a modified version of your service.
a )
var app = angular.module('app',[]);
app.factory('ExampleService',function(){
return {
f1 : function(world){
return 'Hello' + world;
}
};
});
This returns an object, nothing to new here.
Now the way to get this from the console is
b )
var $inj = angular.injector(['app']);
var serv = $inj.get('ExampleService');
serv.f1("World");
c )
One of the things you were doing there earlier was to assume that the app.factory returns you the function itself or a new'ed version of it. Which is not the case. In order to get a constructor you would either have to do
app.factory('ExampleService',function(){
return function(){
this.f1 = function(world){
return 'Hello' + world;
}
};
});
This returns an ExampleService constructor which you will next have to do a 'new' on.
Or alternatively,
app.service('ExampleService',function(){
this.f1 = function(world){
return 'Hello' + world;
};
});
This returns new ExampleService() on injection.
#JustGoscha's answer is spot on, but that's a lot to type when I want access, so I added this to the bottom of my app.js. Then all I have to type is x = getSrv('$http') to get the http service.
// #if DEBUG
function getSrv(name, element) {
element = element || '*[ng-app]';
return angular.element(element).injector().get(name);
}
// #endif
It adds it to the global scope but only in debug mode. I put it inside the #if DEBUG so that I don't end up with it in the production code. I use this method to remove debug code from prouduction builds.
Angularjs Dependency Injection framework is responsible for injecting the dependancies of you app module to your controllers. This is possible through its injector.
You need to first identify the ng-app and get the associated injector.
The below query works to find your ng-app in the DOM and retrieve the injector.
angular.element('*[ng-app]').injector()
In chrome, however, you can point to target ng-app as shown below. and use the $0 hack and issue angular.element($0).injector()
Once you have the injector, get any dependency injected service as below
injector = angular.element($0).injector();
injector.get('$mdToast');
Related
I have an extremely edge case scenario where I have a callback method I have to define during config. Which means no scope, no factories, etc... My work around is to use the root injector ($injector) and get my other modules at runtime.
However, when I call $injector.get('myServiceName') in my call back (after the application is running) I get "unknown provider". The same service has no problem (and actually is) being injected into a before my line of code is running. If I call $injector.get("myServiceNameProvider") then I can get the provider back in my callback.. But another service that only has a factory I can't get at all.
So in this extremely bad practice, how can I snag the service I configured. Heck I can't even seem to get $rootScope..
Angular inits providers first, then factories/services - services are not available in app.config.
You can deo like this:
app.config(...
window.on('scroll', function() {
// this is executed later
$injector.get()...
})
Or use app.run where services are available.
I think I had similar problem few months ago... I need to construct breadcrumbs, but they had to be created in config phase (ui-router). I have done it like this (breadcrumbs-generation-service.provider.js):
var somethingYouHaveToHaveLater = undefined;
function BreadcrumbsGenerationService () {
this.createStateData = createStateData;
function createStateData (arg) {
somethingYouHaveToHaveLater = arg;
};
}
function BreadcrumbsGenerationServiceProvider () {
this.$get = function BreadcrumbsGenerationServiceFactory () {
return new BreadcrumbsGenerationService();
}
}
angular
.module('ncAdminApp')
.provider('BreadcrumbsGenerationService', BreadcrumbsGenerationServiceProvider);
Because service is used inside Angular configs, needs to be injected as provider to be available in config phase: Similar SO Question. Despite the fact is registered as BreadcrumbsGenerationService needs to be injected as BreadcrumbsGenerationServiceProvider to config phase and used with $get():
BreadcrumbsGenerationServiceProvider.$get().createStateData(someParams);
But in controller, inject it without Provider suffix (BreadcrumbsGenerationServic) and it behaves as normal service.
I am trying to use the same service for different modules. There are many modules so i tried to inject them in a parent module. Something like this:
var app=angular.module('myapp',['module_1','module_2',....,'module_n']);
var module_1=angular.module('myapp1',[]);
var module_2=angular.module('myapp2',[]);
var module_3=angular.module('myapp3',[]);
.
.
.
var module_n=angular.module('myappN',[]);
and the service which is common to all the n modules is like this:
.service('myService',function(){
...doing something here...
});
Now I am not able to figure out how to use this service for all the submodules.
With which module should I associate this service ?
I tried doing app.service('myService',function(){...}), but it did'nt work.
Where am I going wrong?
EDIT 1:
Moreover I am trying to share a variable with all these submodules using the service. I am not sure if, I am doing the right thing by using a service for sharing variable or should I use a Provider or Factory for this job.
EDIT 2:
I found these links, but I could not grasp the answer. Refer to them and please provide my answer
How to share a variable between multiple modules in AngularJS
Passing variable between controllers which are on different modules
Lets suppose you want to build a Service to share a certain variable between two Controllers. You should be able to use your Service doing the following:
MyService.js
// Lets suppose you want to share a certain variable between controllers
angular
.module('myApp')
.service('myService', function () {
// If you wish you can inject and use $scope
var vm = this;
// Variable to share
vm.sharedItem;
// Method to set a certain value into a variable
function setItem(item){
vm.sharedItem = item;
}
// Method to get that variable
function getItem(){
return vm.sharedItem;
}
// Exposing your methods
return {
setItem : setItem
getItem : getItem
}
});
SetController.js
angular
.module('myApp')
.controller('SetController', SetController);
// Inject your Service
function SetController(myService) {
var vm = this;
// variable used to set the value
vm.setMe = 'hello';
// Call `setItem()` method from `myService` -> sharedItem will get setMe value
myService.setItem(vm.setMe);
console.log("Set shared item "+vm.setMe);
};
GetController.js:
angular
.module('myApp')
.controller('GetController', GetController);
// Inject your Service
function SetController(myService) {
var vm = this;
// variable used to get shared the value
vm.getMe= null;
/* Call `getItem()` method from `myService` to get the shared
* value and assign it to `getMe`*/
vm.getMe = myService.getItem();
console.log("Got shared item "+vm.getMe);
};
I remind you can access this.var in your view using controllerName.var. It is a good solution to make sure you are using a certain controller. You can always use $scope if you wish.
I hope I've been helpful.
Trying to test a directive that does the following:
var inputs = angular.element(document.body.querySelectorAll('input[ng-model]', elem));
// [code relying on found elements]
Running inside karma/jasmine/phantomjs, this fails because it seems that document returns the document that contains the test, rather than the compiled template. Is there some way to mock this functionality so it works as expected (for my use case) or some other way to query for those elements?
PS: The elements that need to be located are in no known relation to the element that the directive is applied to.
You can use $document instead of document then mock it in your tests.
See Angular js unit test mock document to learn how to mock $document.
The last update in this answer did the trick for me he basically is using the $document service, which is like a wrapper over jQuery and then you can append elements to the body directly and test them:
I'll quote his answer:
UPDATE 2
I've managed to partially mock the $document service so you can use
the actual page document and restore everything to a valid state:
beforeEach(function() {
module('plunker');
$document = angular.element(document); // This is exactly what Angular does
$document.find('body').append('<content></content>');
var originalFind = $document.find;
$document.find = function(selector) {
if (selector === 'body') {
return originalFind.call($document, 'body').find('content');
} else {
return originalFind.call($document, selector);
}
}
module(function($provide) {
$provide.value('$document', $document);
});
});
afterEach(function() {
$document.find('body').html('');
});
Plunker: http://plnkr.co/edit/kTe4jKUnypfe6SbDECHi?p=preview
The idea is to replace the body tag with a new one that your SUT can
freely manipulate and your test can safely clear at the end of every
spec.
I am working on a utility function to list dependencies between angular modules...
function getModules(name){
var module = angular.module(name), out = {};
module._invokeQueue.forEach(function(value){
value[2][0] && console.log(module.name, value[1] + ": " + value[2][0]);
})
out[name] = module.requires.length ? _.map(module.requires, getModules) : [];
return out;
}
This gives me a list of all the registered injectables, and what module they were registered by, and also a list of the dependencies between modules, which is all working fine.
How can I also list use of each injectable
If I have a service called user which was created in myApp module, I want to know where it is being injected. I would be happy with detecting only when injected in function declaration, and not cases where $injector service might have been called directly.
I can't even figure out the right terms to search for this in google.
clarification:
I want to get a list of services/controllers etc... that load another service/constant etc...
... so if I have a service user, I want to get a list of all the other services/controllers that use it...
angular.module('myApp').controller('SuperCtrl', function(user){ /* this one has used user */ });
debug_helper.getUsesOf('user');
// I want this to return ['SuperCtrl']
Did you mean the caller? Angular #injector
Methods
get(name, [caller]);
Return an instance of the service.
or if service exists
has(name);
Allows the user to query if the particular service exists.
I have the following test case:
it('should return id if the post is successful',function(){
var result = {
id : "123"
};
ctrl.saveCallback(result);
expect(ctrl.method.id).to.equal("123");
});
Where ctrl.saveCallback copies the result.id into the method.id on the ctrl and then shows the success banner. On the success banner, we are using the translate filter to translate the message before showing it.
Function:
.....
ctrl.method.id = result.id;
magicallyShowOnScreen($filter('translate')('MESSAGES.SUCCESS'));
....
magicallyShowOnScreen is a service that shows whatever string we pass onto the screen, and that has been injected into beforeEach.
Can someone please point in the right direction as to how should I test or mock out this $filter('translate') ?
Preface: I'm not familiar with Mocha.js or how one would create spies however you would still inject them or similar mock objects the same way as you would for other testing frameworks.
Below is a Jasmine example, I hope it helps.
When you bootstrap your module (using the module / angular.mocks.module) function, you should provide your own version of $filter which should be a mock / spy that returns another mock / spy. For example
var $filter, filterFn;
beforeEach(module('myModule', function($provide) {
$filter = jasmine.createSpy('$filter');
filterFn = jasmine.createSpy('filterFn');
$filter.and.returnValue(filterFn);
$provide.value('$filter', $filter);
});
Then, in your test, you can make sure $filter is called with the right arguments, eg
expect($filter).toHaveBeenCalledWith('translate');
expect(filterFn).toHaveBeenCalledWith('MESSAGE.SUCCESS');
expect(magicallyShowOnScreen).toHaveBeenCalled(); // assuming this too is a spy