403

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?

Matt
  • 74,352
  • 26
  • 153
  • 180
justGoscha
  • 24,085
  • 15
  • 50
  • 61

4 Answers4

735

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()

justGoscha
  • 24,085
  • 15
  • 50
  • 61
  • Interesting. So how come it could be done on angular docs.. which incidentally also has ng-app defined on html. – ganaraj Mar 21 '13 at 12:13
  • 73
    I also had to do this to make it work. BTW, `angular.element('*[ng-app]').injector()` should work for all cases. – Francesc Rosas Apr 06 '13 at 16:43
  • 4
    If you get error 'selectors not implemented' executing angular.element('html') then you can use Chrome $0 feature. Select html element, go to console and run angular.element($0).injector() – Marek Jul 24 '13 at 14:49
  • 9
    `document` also works: `angular.element(document).injector().get('serviceName')` – Tamlyn Sep 13 '13 at 11:11
  • 1
    FYI I had to use document.body on chrome – Kevin Feb 27 '14 at 01:20
  • 5
    FYI I wanted to use $location service, but eventually i needed to wrap it in scope.apply. I know this is well documented, but it had slipped my mind. In one line angular.element(document).scope().$apply(angular.element(document).injector().get('$location').path('/my/angular/url')) – acid_crucifix May 07 '14 at 18:00
  • 1
    angular.element(document.querySelector('*[ng-app]')).injector().get('myService') works for me without jQuery. – oldwizard Aug 11 '14 at 11:54
  • I was wondering about how to do this with filters. I didn't find much via internet search, then I realized $filter is just a service that can be called similarly. Tried to help Angular beginners by setting this up: http://stackoverflow.com/a/37399332/3232832. Hope that helps. Great question and answer, that is a very handy Angular tool to have! – ryanm May 23 '16 at 19:51
  • 1
    Awesome! Just created a Chrome snippet with a `recompile` function. This way I can do `recompile($0)` and have Angular recompile only the needed elements and avoid reloading the whole page. – Samir Aguiar Dec 15 '16 at 16:15
  • It does not work when the DOM has not been not constructed yet. – carbolymer Feb 11 '17 at 13:11
  • Make sure you have debug info enabled too: `angular.reloadWithDebugInfo();` – Zymotik Apr 04 '19 at 15:19
  • for me only few service return an object , the current service that i want to access is just sending whatever there is in return of that service. angular.element(document.body).injector().get('modalizer') ƒ (modalizerName) { cov_5tvztikwe.f[23]++; cov_5tvztikwe.s[64]++; if (!instances[modalizerName]) { cov_5tvztikwe.b[7][0]++; cov_5tvztikwe.s[65]++; instances[modalizerNam… – rohitwtbs May 03 '20 at 22:34
25

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.

ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • 4
    when i do `var $inj = angular.injector(['app']);` then the console throws an `Error: Unknown provider: $filterProvider from app` in one app and `Error: Unknown provider: $controllerProvider from app` in another app... – justGoscha Mar 20 '13 at 16:54
  • @JustGoscha How is your app configured? i.e How does a line ( that looks like ) var app = angular.module('app',[]); look like in your app. – ganaraj Mar 20 '13 at 16:59
  • I'm not completely understanding the question.. it looks just like you say `angular.module('app',[]);` and then there are services, controllers etc in different files and they are all defined like `angular.module('app').factory('FeatureRegistry',function(){//code here});` for example – justGoscha Mar 20 '13 at 17:06
  • @JustGoscha Here is what I did to test. I went to http://docs.angularjs.org/api in chrome. Opened the console. Typed the code in section a of my answer and then typed the code in section b.. You should see Hello World.. Can you attempt that ? – ganaraj Mar 20 '13 at 17:14
  • @JustGoscha - Yes, this is because it ignores the lifecycle of angular. It doesn't wait for services to be made before allowing it to run, so if the service is declared underneath the injector call, it will not see that the service was defined. – Jimmyt1988 Jun 15 '21 at 11:23
14

@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.

Community
  • 1
  • 1
boatcoder
  • 17,525
  • 18
  • 114
  • 178
5

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');

enter image description here

Faiz Mohamed Haneef
  • 3,418
  • 4
  • 31
  • 41