I got services in angular that I need to be configurable, this way I can remove coupling from my app and share some of my code.
My question is how do you achieve this ?
With module.value() and dependancy injection ? Then how do you get a default value ? How do you make a value calculated at init and not hardcoded ?
With a another service ? How do you make that generic ?
Do you have a code exemple, because I'm a bit lost with this.
In order to create a configurable service, you need to use a provider, see: http://docs.angularjs.org/guide/providers. Providers allow you to configure the factory or service in the module's config block before it is instantiated. For a simple example, see http://plnkr.co/edit/QMLBBQ3obE90FtCA2eBu
For a more complete/complex example of how to use configurable services, I would suggest looking at the Restangular project, which also demonstrates a method of providing default values that can be overriden by the app developer.
I believe something like this should work within your service, if you were to use .value() to declare some objects for injection
var myLocalValue = myInjectedValue || "Some default value";
I'm not super javascript savvy but this answer on SO seems to elude to the fact that this will work too:
JavaScript OR (||) variable assignment explanation
I made a fiddle here to test it out it seems to work how I would expect:
http://jsfiddle.net/u3MY8/1/
JavaScript
var app = angular.module("myapp",[]);
// define a value
app.value('myThing', 'weee');
// use it in a service
app.factory('myService', ['myThing', function(myThing, myOtherThing){
var myLocalOtherThing = myOtherThing || "some default";
return {
whatsMyThing: function() {
return myThing; //weee
},
whatsMyOtherThing: function() {
return myOtherThing;
},
whatsMyOtherThingWithDefault: function() {
return myLocalOtherThing;
}
}
}]);
// use it in a controller
app.controller('someController', function($scope, myService) {
$scope.foo = myService.whatsMyThing(); //weee
$scope.bar = myService.whatsMyOtherThing(); //""
$scope.baz = myService.whatsMyOtherThingWithDefault(); //some default
});
HTML
<div ng-app="myapp" ng-controller="someController">
<ol>
<li>{{foo}}</li>
<li>{{bar}}</li>
<li>{{baz}}</li>
</ol>
</div>
To note this only works if myOtherThing isn't injected, if you list it to be injected but it hasn't been defined you'll run into errors, but I'm not sure exactly what the use case is.
Related
I have a code which works for both factory and service :
I am confused at what circumstances I need to use which code.
This code output my value in console.log using service
var mod = angular.module("MyModule", []);
mod.service("myService", function() {
console.log("this is service");
return {
getvalue : function() {
return "My value";
}
};
});
mod.controller("MyController", function(myService) {
console.log("MyController - myFactory: " + myService.getvalue());
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyModule">
<div ng-controller="MyController"></div>
</div>
This code I have just change mod.service() to mod.factory() and rest of code is same and it output's my value in console.log using factory
var mod = angular.module("MyModule", []);
mod.service("myFactory", function() {
console.log("this is Factory");
return {
getvalue : function() {
return "My value";
}
};
});
mod.controller("MyController", function(myFactory) {
console.log("MyController - myFactory: " + myFactory.getvalue());
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyModule">
<div ng-controller="MyController"></div>
</div>
This blog post by Todd Motto is worth a thousands answers.
Using .factory() gives us much more power and flexibility, whereas a
.service() is essentially the “end result” of a .factory() call. The
.service() gives us the returned value by calling new on the function,
which can be limiting, whereas a .factory() is one-step before this
compile process as we get to choose which pattern to implement and
return.
It explains what is the difference at the source code level.
In real life, my opinion is that it's a matter of taste to use one or the other.
Coming from Java, I like the fact that .service looks like a class with the use of this.
Let me explain
Services
Services provide a method for us to keep data around for the lifetime of the app and communicate
across controllers in a consistent manner.
Services are singleton objects that are instantiated only once per app (by the $injector) and lazyloaded
(created only when necessary). They provide an interface to keep together those methods
that relate to a specific function.
Factory
This service factory function is responsible for generating a single object or function that becomes
this service, which will exist for the lifetime of the app. When our Angular app loads the service, the
service will execute this function and hold on to the returned value as the singleton service object.
The service factory function can be either a function or an array, just like the way we create
controllers:
So i would like you to use factory for your core operations, and call this factory from one service(You need to create one saporate services for this), so this way you can provide more isolation in your app.
Example
*make one factory like :
app.factory('memberServiceFactory', ['$http','$filter', function ($http,$filter) {
return {
getData: function (data) {
return {};
},
addData: function (file, formData) {
},
}
}]);
*make one service like :
app.service('memberListService', ['memberServiceFactory', function (memberServiceFactory){
this.getList = memberServiceFactory.getData;
this.addMember = memberServiceFactory.addData;
}]);
You can see here i am calling factory from services, you can provide encapsulation like this.
I'm building a small two-language app with the use of angular-translate. I want to have a language switcher in every view (controller). I'm trying to figure out how to put the code responsible for language switching into every controller. The code looks like this:
var langSwitch = $Scope.setLang = function (langKey) {
$translate.use(langKey);
};
So far I've figured that I can create a factory that looks like this:
app.factory('langSwitch', function ($rootScope, $translate) {
var langSwitch = $rootScope.setLang = function (langKey) {
$translate.use(langKey);
};
return langSwitch;
});
and inject it into controllers in this maner:
app.controller('HomeCtrl', function (langSwitch) {
// normal controller code here
});
This works but 1) I'm using $rootScope and I have a feeling this is bad practice & 2) jsHint screams that "langSwitch" is not defined. Maybe there is a simpler way to make the function global without putting it into every controller?
I'm still pretty new to Angular so don't scream at me :) Thanks.
edit
My view:
<button ng-click="setLang('en_GB')">English</button>
<button ng-click="setLang('pl_PL')">Polish</button>
Although you got the idea, you overcomplicated things a bit. You could declare the service as follows:
app.service('langSwitch', function ($translate) {
this.setLang = function (langKey) {
$translate.use(langKey);
};
});
And then inject langSwitch in the controller responsible for lang switching, as you already did. No need to inject $rootScope in the service.
You don't need $rootScope indeed unless you need to process some global events in your application. All services and factories in angular are singletons by default. That means once it created, it will be passed as the same instance in every place it is declared as a dependency. So if you want to share data and functionality between different controllers - the services will suit fine. You can change your factory code to:
app.factory('langSwitch', function($translate) {
return {
setLang: function(langKey) {
$trasnlate.use(langKey);
};
};
});
Im going through one of the Pluralsight courses on AngularJS with MVC5 and one of the services has this as a return statement and it looks rather strange. Im not sure what this is doing
return {
insertEmployee: insertEmployee,
updateEmployee: updateEmployee,
getEmployee:getEmployee
};
the insert/update/get names are names of methods in the service, but I dont understand the return statement. What is this doing?
It returns an object with those methods available, example:
var methods = {
insertEmployee: insertEmployee,
updateEmployee: updateEmployee,
getEmployee:getEmployee
}
Now you can do:
methods.insertEmployee(); //etc...
Actually calling a service/factory (Both can be used the same way actually, despite the fact they're not intended to work the same way.) is just calling a function. The function you defined.
If you do this :
Service :
var myfunction = function(){
alert("hello");
}
Without any return you will not see this function in your controller.
Controller :
myservice.myfunction(); will not work.
You need to return an object to be able to use the functions.
Service :
var myfunction = function(){
alert("hello");
}
return {
myfunction: myfunction;
}
This will allow you to use the service this way in your controller :
myservice.myfunction();
I usually prefer to use the object syntax in an other way, but this is just a matter of tastes :
Service :
var service = {};
service.myfunction = function(){
alert("hello");
}
return service;
This will also allow you to use this syntax in the controller :
myservice.myfunction();
Hope it helped, if you have anymore question, feel free to ask.
Actually you can understand it as AngularJS defines a factory like that. It just means: I want to expose the functions insertEmployee, updateEmployee, and getEmployee to the users of this factory.
Please refer to AngularJS: Service vs provider vs factory.
In this way, say, your factory name is myFactory, the usage is like:
angular.module('MyApp')
.controller('MyCtrl', ['$scope', 'myFactory', function ($scope, myFactory) {
myFactory.getEmployee();
}]);
I have defined a service like this :
angular.module('myApp').service('myService', [
'$rootScope',
...
...
I want my service to be instantiated only for new user (i.e. when user.createdAt > today).
So is there a way to conditionally inject my service or at least destroy my service without any side effect if the user is an old one.
You can use the $injector service to get inject-ibles dynamically if you need to:
app.controller('DemoCtrl', function($injector) {
this.clicky = function() {
myFact = $injector.get('myFactory');
myFact();
};
});
app.factory('myFactory', function() {
return function() {
alert('foobar!');
};
});
Here's a full running demo: http://jsbin.com/bemakemaja/1/edit
And the $injector docs: https://docs.angularjs.org/api/auto/service/$injector
As a general guideline though I'd recommend not designing your services such that simply injecting them has side effects.
use factory instead of service, so that you can have some checking logic in your service
angular.module('myApp').factory('myService', function () {
if (newUser) {
return { // your service implementation here
}
}
return undefined;
};
So is there a way to conditionally inject my service or at least destroy my service without any side effect if the user is an old one.
Best to capture this logic inside the service:
class Service{
static $inject = ['$rootScope'];
constructor($rootScope){
if (user.createdAt > today){ /*something*/ }
else {/*other thing*/}
}
}
NOTE: services are singletons so conditional injection doesn't see like a desirable solution.
Also to learn about $inject : https://www.youtube.com/watch?v=Yis8m3BdnEM
I need to write code which accesses data in a backend.
Trouble is that backend is not ready yet. So I want to inject a service which would, possibly by configuration, use the real backend or a mock object.
I think angular services are what I need. But I am not clear how to implement this.
app.factory('myService', function() {
var mySrv;
//if backend is live, get the data from there with $http
//else get data from a mock json object
return mySrv;
});
Somehow I guess I'd have to write two more services, the real one and the fake one, and then call the one or the other in 'myService'?
Maybe I totally misunderstand mocking, but I'd rather not want this to be mocked for test runs (not for unit tests like in this post: Injecting a mock into an AngularJS service - I'd like the app to really use my mock for demo and development purposes.
This is actually where Angular's dependency injection system really comes in handy. Everything in Angular is basically a provider at some level. Methods like service and factory are just convenience functions to avoid boilerplate code.
A provider can be configured during the bootstrap process, which is really handy for setting up scenarios exactly like what you are describing.
At it's simplest, a provider just needs to be a constructor function that creates an object with a $get function. The $get is what creates the actual services, and this is where you can check to see which one to create.
//A simple provider
function DataServiceProvider(){
var _this = this;
_this.$get = function(){
//Use configured value to decide which
// service to return
return _this.useMock ?
new MockService() :
new RealService;
};
}
Now you can register this as a provider with your application module.
angular.module('my-module', [])
.provider('dataService', DataServiceProvider);
And the provider can be configured before it creates the first service instance. By convention, the provider will be available as NAME + 'Provider'
angular.module('my-module')
.config(['dataServiceProvider', function(dataServiceProvider){
//Set the flag to use mock service
dataServiceProvider.useMock = true;
}]);
Now whenever you inject dataService anywhere in your application, it will be using the mock service you provided based on configuration.
You can see a full working example of this in the snippet below.
(function(){
function RealService(){
this.description = 'I\'m the real McCoy!';
}
function MockService(){
this.description = 'I\'m a shady imposter :P';
}
function DataServiceProvider(){
var $this = this;
$this.useMock = false;
$this.$get = function(){
return $this.useMock ?
new MockService() :
new RealService();
};
}
function CommonController(dataService){
this.dataServiceDescription = dataService.description;
}
CommonController.$inject = ['dataService'];
angular.module('common', [])
.provider('dataService', DataServiceProvider)
.controller('commonCtrl', CommonController);
angular.module('provider-app-real-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = false;
}]);
angular.module('provider-app-mock-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = true;
}]);
var moduleNames = ['provider-app-real-service','provider-app-mock-service'];
angular.forEach(moduleNames, function(modName){
//Have to manually bootstrap because Angular only does one by default
angular.bootstrap(document.getElementById(modName),[modName]);
});
}());
<script src="http://code.angularjs.org/1.3.0/angular.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div class="row" id="provider-app-real-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
<div class="row" id="provider-app-mock-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
</div>
Does this need to be a service? If you want to create a mock service then Josh's answer above is perfect.
If your not tied to using a service then I suggest looking at my answer to the following question Mock backend for Angular / Gulp app which also about mocking out a backend. Regardless of wether your backend is created or not mocking it out allows for more stable test runs and development of you app.