Angular IoC best practice - angularjs

What's the best practice in AngularJS when one needs to use a different implementation of a service/factory depending on the environment.
Let's say I have a service MessageService, which is injecting some other service, but based on the device/platform, it should use WebService vs. MobileService.
Here's what I do now:
angular
.module('message')
.service('MessageService', messageService);
var service = 'WebService';
if (mobileDevice) {
service = 'MobileService';
}
messageService.$inject = [service];
function messageService(service) {
service.call(); // use the shared interface for both services inside this service
}
Is there a better - more elegant - way to do this?

You can use the module's 'config' block to do this using a 'provider' like here -
angular.module('app').config(['$provide', '$injector', 'mobileDevice',
function($provide, $injector, mobileDevice) {
$provide.value('MessageService', (mobileDevice) ? $injector.get('MobileService') : $injector.get('WebService'));
}
]);
This is assuming that 'mobileDevice' is an angular constant.
Now you can inject 'MessageService' to do what you wanted.

Related

Angular Controller can inject service just fine but $injector.get can't?

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.

Pass multiple scope members from controller to angular service

I am new to AngularJS world. I am developing AngularJS SPA application. I have a controller paymentController which is going to use an angular custom service paymentService. The paymentController $scope has multiple members like billerId, billAccount, paymentAmount, etc. I want to pass all/most of these members to the function exposed by angular service. I don't know what is the best way to do so. My code is given below:
app.controller("paymentController", function ($scope, $rootScope, paymentService) {
$scope.billerId;
$scope.billAccount;
$scope.paymentAmount;
$scope.feeAmount=1.0;
$scope.platform = 1;
$scope.makePayment = function(){
paymentService.makePayment(/*what should be passed to this function*/);
}
});
Please suggest me the ideal way.
Better way is to create a object with all those properties and pass the object,
$scope.bill ={};
$scope.bill.billerId;
$scope.billAccount;
$scope.bill.paymentAmount;
$scope.bill.feeAmount=1.0;
$scope.bill.platform = 1;
$scope.makePayment = function(){
paymentService.makePayment($scope.bill);
}

Angularjs : Using common service in different modules

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.

Should I attach a config to $rootScope or repeatedly call injected factory?

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.

Angular JS: why the difference between module.config injection and controller injection?

This is something that I could not figure out from digging into the AngularJS code, maybe you can help solve the mystery.
To show it, I added a service to AngularJS seed project:
function MyServiceProvider() {
console.log('its my service');
this.providerMethod = providerMethod;
function providerMethod() {
console.log('its my service.providerMethod');
}
this.$get = $get;
function $get() {
var innerInjectable = {
name: 'stam'
};
return innerInjectable;
}
}
var serviceModule = angular.module('myApp.services', []).
value('version', '0.1').
provider('myservice',MyServiceProvider);
You can see that this provider exposes $get and a certain 'providerMethod'.
Now, for the injection usage:
If we call config, we can inject the whole class and get access to the 'outer' provider method:
serviceModule.config(function(myserviceProvider) {
console.log('myServiceProvider:',myserviceProvider);
myserviceProvider.providerMethod();
});
But when we inject this to a controller (note the Provider-less name), only the $get return value is exposed:
function MyCtrl1(myservice) {
console.log('MyCtrl1.myservice =',myservice,myservice.name);
}
MyCtrl1.$inject = ['myservice'];
Console output follows as it should:
its my service
myServiceProvider:
Constructor {providerMethod: function, $get: function}
its my service.providerMethod
MyCtrl1.myservice = Object {name: "stam"} stam
Can any one explain the difference? The reason?
many thanks for any thought
Lior
PS: I've seen this technique in angular-ui new ui-router (excellent project!). I need access to the outer provider class to do injection in jasmine and other places - to no avail
A provider is responsible for creating instances. In your example, you created a provider explicitly, but the truth is that every service has a provider, even if it's created automatically for it. [module].service() and [module].factory() are just shortcuts for [module].provider().
[module].config() is run during provider registrations and configurations so you get a change to access providers and do stuff with them. It's a place for configuration of things, hence the name.
From the documentation (http://docs.angularjs.org/guide/module):
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured.
Controllers, in the other hand, are instantiated AFTER services have been configured, so you're not supposed to mess with providers anymore. Everything has already been configured. You're ready to get their products now. In this phase, the injector just can't inject providers anymore, just instances (services) created by them.
If you register a service myService...
myModule.service('myService', function() {
// this is your service constructor
});
then you can access its provider, myServiceProvider, in a config function...
myModule.config(function(myServiceProvider) {
// to stuff with your provider here
});
but by the time controllers are being instantiated, you're supposed to ask for services, not their providers, so this will not work...
myModule.controller(function(myServiceProvider) {
...
});
whereas this will be fine...
myModule.controller(function(myService) {
...
});
If you're finding yourself needing to do configuration in a controller, you should stop and rethink the place of responsibilities.
From the Angular mailing list I got an amazing thread that explains service vs factory vs provider and their injection usage. I decided to put it in its own question here
the specific answer is: it is so by design, to allow configuration of the provider at config time.

Resources