AngularJS - obtaining an instance of an injected module/service - angularjs

In this post #yair-tavor posted snippets related to his SOAP interceptor module. Unfortunately, there is no clear and obvious example provided for obtaining an instance of the created 'myModule' object. I've already downloaded Yair's code from the Fiddle provided in that post, and included it in the HTML web page I'm developing. Being completely new to the AngularJS world (and learning more every minute!) I'd like to ask precisely HOW to obtain a local instance of that module/service.
I see that Yair's code includes this:
/**
* To be used by angular, this method retrieves new
* {{#crossLink "rtv.data.soap-interceptor"}}{{/crossLink}} instance.
* #method $get
* #returns {SoapInterceptor}
* #for myModule.soap-interceptorProvider
*/
providerInstance.$get = function(){
return new SoapInterceptor();
};
}]);
I've bookmarked this very helpful SO post for some detailed review, tomorrow:
AngularJS: Service vs provider vs factory
So, I've tried to use the following syntax to obtain an instance, but it appears to return a module full of null objects, rather than the module I thought I was loading. :( Attempting to call soap.setWSDL using this module returns "TypeError: undefined is not a function".
var soapModule = angular.module('myModule.soap-interceptor', [] );
soapModule: {
_invokeQueue:
[ ]
_runBlocks:
[ ]
requires:
[ ]
name: soapModule.soap-interceptor
provider: null
factory: null
service: null
value: null
constant: null
animation: null
filter: null
controller: null
directive: null
config: null
run: null
}
Nor does calling it without the second argument, which returns an error saying the module is not available.
var soapModule = angular.module('myModule.soap-interceptor');
Error: [$injector:nomod] Module 'myModule.soap-interceptor' is not available!
You either misspelled the module name or forgot to load it. If registering a
module ensure that you specify the dependencies as the second argument.
So, how exactly SHOULD I be loading and hooking into Yair's soap-interceptor module? I need a valid non-null instance of it, in order to successfully execute the "setWSDL" method which loads the WSDL content.

Related

Adding an attribute to a provider after config

I am using ng-flow, to upload files with a servlet but as I was securing the servlet I realized I need to pass the token to the headers so It would work and be secure. The problem is that ng-flow's settings are declared on a provider inside a .config box. And as I learned the hard way you can't inject stuff on .config because the injections are created after config.
angular.module('UploadModule', [ 'ngResource','flow' ,'AuthModule']).config(
[ 'flowFactoryProvider',function(flowFactoryProvider,$provide) {
//AuthService.getKeycloak();
flowFactoryProvider.defaults = {
target : '/ng-flow-java/upload',
permanentErrors : [ 500, 501 ],
maxChunkRetries : 1,
chunkRetryInterval : 5000,
simultaneousUploads : 4,
progressCallbacksInterval : 1,
withCredentials : true,
method : "octet",
headers : {'Authorization', 'Bearer + ' token}
};
flowFactoryProvider.on('catchAll', function(event) {
console.log('catchAll', arguments);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
} ]);
I am really new to angular so I am looking for a way reassemble this code so I can add the token from my user.
Thanks
I think I don't understand what exactly is the problem but:
Actually each angular service x has a provider function. This function is a constructor function and will be instanciated by angular and its result is injectable in config blocks with name xProvider. This object should have a special $get function which is actually the factory for the service, meaning that it will be called to get the single instance of the service, when it is injected into some controller, directive, etc for the first time.
So you should think of an angular service as a function like this:
function SomeServiceProvider(){
this.$get = function SomeServiceFactory(){
}
this.someConfigurerFunction(){
}
}
which is registered with provider API:
someModule.provider("someService", SomeServiceProvider);
and angular internally will execute something like new SomeServiceProvider() and save it and makes in injectable in config blocks under the name of someServiceProvider. Further, when you ask for "someService", in a directive, controller, etc, for the first time, the injector will call something like new someServiceProvider.$get(), return its result to you and saves it in its registry for further injections.
When you use other higher level angular module APIs, like factory or service like this:
someModule.factory("anotherService", function AnotherServiceFactory(){
// code for creating service
});
the provider function is generated for you with only a $get function, so you don't see the provider function, but still there is a provider function for your service like this:
function AnotherServiceProvider(){
this.$get = AnotherServiceFactory;
}
and it's used as constructor to instantiate anotherServiceProvider which is injectable in config blocks.
You can find out good information about angular services in angular documentations for $provide and angular documentations for module API
Side Notes:
Good services usually come with a provider that enables you to configure service, but if not, you still can intercept the creation of the service with decorators and make service to work as you want.
The process of instantiating objects like services is not exactly like what I've said here (I mean new ...), but it doesn't changes the concepts described here.

Mock with jasmine

I want to isolate my angular unit specs.
My code base consists of a factory and a controller:
.factory('ServiceData', ->
{ message: 'im data from a factory service' }
)
.controller('Controller', ['$scope','ServiceData',($scope, ServiceData)->
$scope.serviceData = ServiceData
And here is a spec for the controller:
it 'should get data from a service', ->
spyOn(ServiceData, 'message').andReturn('im from a mock') # this doesn't work
expect($scope.serviceData).toEqual 'im from a mock'
The spec doesn't work, because I get the following error:
ReferenceError: ServiceData is not defined in ...controller_spec.js
I want to isolate my controller spec from my factory. How do I achieve this?
Also, this is a trivial example, but in future I will have very complex factories that get data from the server and all sorts. So I want to know how to stub them out.
I also might want to test how my controller behaves based on different things that are returned by the mocked out function, so I definitely want to use this approach.
I've tried to using $provide, but to much success
beforeEach ->
module 'App', ($provide)->
$provide.value 'serviceData', { message: 'im from a mock' }
results in:
Error: [ng:areq] Argument 'fn' is not a function, got Object
I mean...I just don't even know
Quote:
it 'should get data from a service', ->
spyOn(ServiceData, 'message').andReturn('im from a mock') # this doesn't work
expect($scope.serviceData).toEqual 'im from a mock'
The spec doesn't work, because I get the following error:
ReferenceError: ServiceData is not defined in ...controller_spec.js
/quote
I think it should be something more like this:
it 'should get data from a service', -> //this is coffeescript syntax you're using I assume
$scope.serviceData = ServiceData.serviceData
expect($scope.serviceData).toEqual 'im from a service'
//where ServiceData is defined as a service that returns a Plain Old Javascript Object
//and that service returns an object in format: { serviceData: 'im from a service' }
If I understand what you are trying to test

How to use constant module in angular?

In my angular application I have a settings module that I made a constant. I figured that it should be a constant because the constants get applied before other provide methods.
I also have 2 services: getUserIdService and getTokenService that get me userId and token respectively.
angular.module('app').constant('settings', {
streamServer: 'http://someurl.com:9009',
userId: /* $getUserIdService.getUserId() */
token: /* $getTokenService.getToken() */
});
From what I see in the api constant doesn't have a constructor so I can't pass in any dependencies (getUserIdService, getTokenService)
This module needs to be globally available and it doesn't have to be a constant. I just need it to get initialized before any other module.
How can I do this?
Use the angular module run method to initialize your data. You can inject your services into the run method and use it to initialize your data
Sample demo: http://plnkr.co/edit/TW5r7YM5gkevWr2hjmZs?p=preview
JS:
var app = angular.module('plunker', []);
app.value('settings', {
streamServer: 'http://someurl.com:9009',
userId:null ,/* $getUserIdService.getUserId() */
token: null/* $getTokenService.getToken() */
}).run(function($http,$timeout,settings){//inject your services here
settings.userId = 'guru';//call services to get the user id
settings.token = 'tkn39';
});

How to check for the existence of a module without an error being raised?

In Angular 1.2, ngRoute is a separate module so you can use other community routers like ui.router instead.
I'm writing an open-source module that aims to work for multiple different router implementations. So how can I check which router is loaded or exists?
I'm doing the following inside a factory in my module, but it does not work the way I expect it to:
if (angular.module("ngRoute"))
// Do ngRoute-specific stuff.
else if (angular.module("ui.router"))
// Do ui.router-specific stuff.
It raises an error for whichever module is not loaded. For example, if the app is using ui.router, then the following error is raised for the ngRoute check:
Uncaught Error: [$injector:nomod] Module 'ngRoute' is not available!
You either misspelled the module name or forgot to load it. If
registering a module ensure that you specify the dependencies as the
second argument.
I am not aware of a way of checking without an error being raised; however, notice that the issue is that it was an Uncaught Error, not that an error was thrown. The pattern for catching such an error is the following.
try { angular.module("ngRoute") } catch(err) { /* failed to require */ }
If an error is caught, you can try the other module, and if not, you can use the first.
If your behavior will be the same for each module, you could do something like the following, in which we define a function which will attempt the first of the listed module names, and if an error is thrown, try the next option.
var tryModules = function(names) {
// accepts a list of module names and
// attempts to load them, in order.
// if no options remain, throw an error.
if( names.length == 0 ) {
throw new Error("None of the modules could be loaded.");
}
// attempt to load the module into m
var m;
try {
m = angular.module(names[0])
} catch(err) {
m = null;
}
// if it could not be loaded, try the rest of
// the options. if it was, return it.
if( m == null ) return tryModules(names.slice(1));
else return m;
};
tryModules(["ngRoute", "ui.router"]);
I would test for the service instead of the module itself.
// In controller
if($injector.has('$route')){
}
if($injector.has('$state')){
}
// In angular config
if($injector.has('$routeProvider')){
}
if($injector.has('$stateProvider')){
}
The original answer is legit. However, as an alternative, I wrote this when I needed to "find or create" the modules. There's a number of use cases, but generally, it lets you not have to worry about file load order. You could either put this in a initialModules.js... or the top of all your individual service/directive files start with something like this. This little function works like a charm for me:
var initialModules = [
{name: 'app.directives', deps: ['ui.mask']},
{name: 'app.services'},
{name: 'app.templates'},
{name: 'app.controllers'}
];
initialModules.forEach(function(moduleDefinition) {
findOrCreateModule(moduleDefinition.name, moduleDefinition.deps);
});
function findOrCreateModule(moduleName, deps) {
deps = deps || [];
try {
angular.module(moduleName);
} catch (error) {
angular.module(moduleName, deps);
}
}
///// OR... in like "myDirective.js"
findOrCreateModule('app.directives').directive('myDirective', myDirectiveFunction);
If you decorate angular.module to store the names in an array then you could just check if the array contains your module name.
Decorate angular.module
See #dsfq's answer on SO.
This needs to happen after angular is loaded but before you start loading any angular modules.
Check for your module
if(angular.modules.indexOf("ngRoute") > -1) ...
The problem of automatically load or create a module could be better solved by something like gulp-angular-filesort, though.
It works really flawlessly.
From gulp-angular-filesort github page:
Automatically sort AngularJS app files depending on module definitions and usage
Used in conjunction with gulp-inject to inject your AngularJS application files (scripts) in a correct order, to get rid of all Uncaught Error: [$injector:modulerr].
Disclaimer: I'm not affiliated with gulp-angular-filesort, I only use it with a lot of profit.
A much better solution is to simply do your check when the module is created. You just need a utility function to add a callback.
//create a utility function to add a callback to object methods
//here we are making it a method of the underscore or lowdash object
//but it could be added to the angular global object or anything else
_.addCallBack = function (obj, originalMethodName, callBackMethod, context){
var fnOriginal = obj[originalMethodName],
outcome;
context = context || obj;
obj[originalMethodName] = function () {
var outcome = fnOriginal.apply(this, arguments);
callBackMethod.apply(this, arguments);
return outcome;
};
};
_.addCallBack(angular, "module", function(sModuleName, asDependencies){
if(_.contains(asDependencies, "ngRoute")){
//your logic here
//just loop through if you don't use underscore or lowdash
}
});
AngularJS 1.6.3 and up has a way to check if a module is loaded via the $injector service.
Also added in 1.6.7 was the ability to load new modules which may be of interest to some.

How can I test an AngularJS service from the console?

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

Resources