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';
});
Related
My use case is: we have several helper classes, A and B, that are services, A depends on B, and I wanted to make them providers so that they can be used in .config phase.
I followed this SO answer to load a provider inside a provider.
As you can see here, it works:
http://plnkr.co/edit/SIvujHt7bprFumhxwJqD?p=preview
var coreModule = angular.module('CoreModule', []);
coreModule.provider('Car', function() {
//CarProvider.engine
this.engine = 'big engine';
//Car
this.$get = function() {
return {
color: 'red'
};
};
});
coreModule.provider('ParameterService', ['$injector', function($injector) {
try {
var CarProvider = $injector.get('CarProvider');
this.deepEngine = CarProvider.engine;
console.log('deepEngine = ' + this.deepEngine);
} catch (e) {
console.log("nope!")
}
// ParameterService
this.$get = function() {
return {};
};
}]);
coreModule.config(function(CarProvider) {
console.log('configEngine = ' + CarProvider.engine); // big engine
});
This works if I have Car and ParameterService in one file in this order.
However when I split Car and ParameterService into multiple files on disk, or I define ParameterService before Car in the same file, $injector.get('CarProvider') inside ParameterService fails.
How do I fix the issue?
I want to have one provider/service per file and I don't understand what is missing.
The order in which the services are defined doesn't matter during run phase, where service instances are injected. But it does matter during configuration phase, where service providers are injected, i.e. in provider constructors and config blocks.
Providers and config blocks are executed in the order in which they are defined. If Car provider is defined after ParameterService provider or config block, CarProvider doesn't exist at the moment when those two are executed.
To avoid potential race conditions, one module per file pattern should be followed. This allows to keep the app highly modular (also beneficial for testing) and never care about the order in which the files are loaded. E.g.:
angular.module('app', ['app.carService', 'app.parameterService']).config(...);
angular.module('app.carService', []).provider('Car', ...);
angular.module('app.parameterService', []).provider('ParameterService', ...);
Module parts are executed in the order in which the modules are defined in angular.module array hierarchy, from children to parents.
The decision if config block needs its own module depends on what it does (mostly for testing reasons).
It is possible to have providers in different files. You just need to attach them to the first module that you created.
If your markup looks like this:
<script src="coreModule.js"></script>
<script src="parameterService.js"></script>
Then, in coreModule.js, define your module:
angular.module('CoreModule', [])
.provider('Car', function() {
...
}
Remember, the second parameter ([]) tells angular to create a new module.
Then, declare your other provider in a different file, and attach it to your existing 'CoreModule' module:
angular.module('CoreModule')
.provider('ParameterService', ['$injector', function($injector) {
...
}
Notice that we are only passing one parameter to .module(). This tells angular to add your provider to an existing module.
Plunkr Demo
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.
I have some data in my firebase server that provides settings for client apps.
I've implemented a factory that gets this data from the server.
My question is regarding design - should I get this data once and application start and attach it to rootscope and pass that to all controllers needing it or inject my factory into every controller that uses the config data and internally in the factory store the config locally?
// this factory allows getting various global app settings and constants
.factory('ConfigFactory', ['$firebaseObject', 'FirebaseConfig',
function($firebaseObject, FirebaseConfig) {
'use strict';
var configs = new Firebase(FirebaseConfig.baseUrl + "/configs");
var _configs = [];
return {
getConfig: function(configName, user_id){
if( !_configs[configName]){
_configs[configName] = $firebaseObject(configs.child(configName));
}
return _configs[configName];
}
};
}
])
The good practice is to use a service and inject this service where you need that information.
Note that what you're defining is not a factory, but a service. The factory is the function used to create the service, and the fact that you're using factory() rather than service() or provider() to define this service is an implementation detail.
The service is thus named badly. In should be named "configuration", rather than "ConfigFactory". Services usually start with a lowercase letter.
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.
I would like to provide simple constant values such as names, emails, etc to use in my jasmine unit tests.
A similar question was asked here: AngularJS + Karma: reuse a mock service when unit testing directives or controllers
In c# I would create a static class to hold little snippets of mock data. I can then use these values throughout my unit tests, like this:
static class SampleData
{
public const string Guid = "0e3ae555-9fc7-4b89-9ea4-a8b63097c50a";
public const string Password = "3Qr}b7_N$yZ6";
public const string InvalidGuid = "[invalid-guid]";
public const string InvalidPassword = "[invalid-password]";
}
I would like to have the same convenience when testing my AngularJS app using Karma / Jasmine.
I know that I can define a constant object against my angular app, I already do this for constants I use in the real code, like this:
myApp.constant('config', {apiUrl:'http://localhost:8082'})
I could add another constant just like this but only containing sample data values for use in my unit tests, like this:
myApp.constant('sampleData', {email: 'sample#email.com'})
I could then just inject the mock constant object into my tests and off I go, like this
describe 'A sample unit test', ->
beforeEach -> module 'myApp'
beforeEach inject ($injector) ->
#sampleData = $injector.get 'sampleData'
email = #sampleData.email
# etc ...
However this seems a bit fishy to me. I don't want my production code to contain sample data that is only required by my unit-tests.
How would you conveniently provide your angular / jasmine unit tests with re-usable sample data values?
Thanks
There are two ways of doing this:
spy on function calls and return fake values.
create mock classes (and possibly mock data to initialise them) and load them wherever you need
The first one is alright when you only have to fake a few calls. doing that for a whole class is unsustainable.
For example, let's say you have a service that builds some special URLs. If one of the methods depends on absUrl, you can fake it by spying on the method in the $location object:
describe('example') function () {
beforeEach(inject(function () {
spyOn($location, 'absUrl').andCallFake(function (p) {
return 'http://localhost/app/index.html#/chickenurl';
});
}));
it('should return the url http://www.chicken.org') ... {
// starting situation
// run the function
// asserts
}
Now let's say that you have a Settings Service that encapsulates data like language, theme, special paths, background color, typeface... that is initialised using a remote call to a server.
Testing services that depend on Settings will be painful. You have to mock a big component with spyOn every time. If you have 10 services... you don't want to copypaste the spyOn functions in all of them.
ServiceA uses Settings service:
describe('ServiceA', function () {
var settings, serviceA;
beforeEach(module('myapp.mocks.settings')); // mock settings
beforeEach(module('myapp.services.serviceA')); // load the service being tested
beforeEach(inject(function (_serviceA_, _settings_) {
serviceA = _serviceA_;
settings = _settings_;
}));
container
for this test suite, all calls to the Settings service will be handled by the mock, which has the same interface as the real one, but returns dummy values.
Notice that you can load this service anywhere.
(If, by any reason, you needed to use the real implementation, you can load the real implementation before the mock and use spyOn for that particular case to delegate the call to the real implementation.)
Normally you'll place the mocks module outside of the app folder. I have a test folder with the unit tests, e2e tests and a lib folder with the angular-mocks.js file. I place my mocks there too.
Tell karma the files you need for the tests:
files: [
'app/lib/jquery/jquery-1.9.1.js',
'test/lib/jasmine-jquery.js',
'app/lib/angular/angular.js',
'app/lib/angular/angular-*.js',
'test/lib/angular/angular-mocks.js',
'test/lib/myapp/*.js', /* this is mine */
'app/js/**/*.js',
'test/unit/**/*.js'
],
The file tests/lib/myapp.mocks.settings.js looks just like any other module:
(function () {
"use strict";
var m = angular.module('myapp.mocks.settings', []);
m.service('settings', function () { ... })
})
Second problem (optional): you want to change quickly the dummy values. In the example, the settings service fetches an object from the server when it is instantiated for the first time. then, the service has getters for all fields. This is kind of a proxy to the server: instead of sending a request everytime you need a value, fetch a bunch of them and save them locally. In my application, settings don't change in the server in run-time.
Something like:
1. fetch http://example.org/api/settings
2. var localSettings = fetchedSettings;
3 getFieldA: function() { return localSettings.fieldA; }
Go and pollute the global namespace. I created a file settings.data.js with a content like:
var SETTINGS_RESPONSE = { fieldA: 42 };
the mock service uses this global variable in the factory to instantiate localSettings