At Reonomy, we scaffold many of our client-side products with the Angular 1.* framework. One of our trickiest tasks is hooking up a detailed search form to validate and submit dynamically while the user is populating it.

We’ve had to get very clever about how and why we use services to keep things optimized and readable. Doing a deep dive into the world of Angular services has revealed some insights that are not obvious in the current Angular documentation.

For a more detailed introduction of the Reonomy product, read on here.

This article seeks to explain the different service types in Angular 1.* and provide a proactive approach to using them semantically and with purpose.

It is by no means a hierarchically correct way of doing things, but an approach that can help you and others who touch your code read and refactor with less confusion.

The Angular docs describe services like this:

Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.

I like to believe this is intentionally abstract or vague, and that the smart folks on the Angular team are leaving it open to the developer to discover how best to use their tools.

The Angular docs tell us that services are lazily instantiated and that they are singletons, but start to thin out when describing the different service types. Add to that the fact that there is a service service, and things get confusing quick.

Different service types include:

  • provider
  • factory
  • service
  • value
  • constant
  • decorator

To add to the confusion, when you begin to search for tutorials/examples, you’ll see services being used for different purposes, and get some strong opinions on correct use cases.

Some more common use cases are:

  • model
  • promise
  • constructor
  • constants

I’ve heard arguments over why using services for one purpose over another is the right way to go. I’d like to suggest that all purposes are correct, and that it is just up to the developer to use them with intention while making it obvious when and why you are using a service in a certain way.

View image on Twitter

Different Types of Services

There are many articles that break down in-depth what a service is and the difference between service types. While that’s not the purpose of this article, I do want to touch on them.

For a more in depth breakdown click any of these links:

Generally speaking, I have yet to see a reason why factory should be used over service or vice-versa. It is nice however to pick one within an organization and stick with it.

Providers allow you to add methods to configure a service before your app inits.

These are available to you in the angular module config method.

A good example of this is the angular-bootstrap tooltip directive, which has a $tooltipProvider which exposes a setTriggers() method.

This way if you don’t like the directives initial default, you can decide what event will be the default trigger for a tooltip before the app is initialized.

Keeping in line with that thinking, it’s reasonable to use a provider if you’re creating a service that will be shared across apps that may have different desired default behavior.

Values and constants allow you to create light-weight services that do not require a function. They can be simple variable types like strings, numbers, booleans and object literals.

Other than syntactical sugar™, the main difference between values and constants is that a value can be decorated before the app has init, and a constant can not.

This sort of helps enforce the idea of a constant being a constant and not changing over time. Though there is nothing in the Angular 1.* code that stops you from changing a constant once the app has initialized.

Provider

Factory, service and value inherit from provider. The provider service is the first service an Angular app has access to. This means you have access to provider before the app has initialized.

This is advantageous if you wish to configure values in your service before the app has init. Within the provider structure you create a factory and a service. Providers are available inside the config method of a module.

I will go deeper into configuring providers in the Decorating Services section below.

angular.module(‘myApp’, [])

.provider(‘myService’, function() {

var configurableVar = ‘foo’;

return {
// all keys in this object are provider level, and are available before the app inits
setConfigurableVar: function(val) {

configurableVar = val;
},
$get: function() {
// this function is the factory
return {
// this object is the service
configurableVar: configurableVar
};
}
};
})

.config(function(myServiceProvider) {

myServiceProvider.setConfigurableVar(‘bar’);
})

Factory

Factory is the next step up the chain. A factory inherits from a provider and returns a service.

While factories and services are different under the hood, the difference between using them in practice is mainly syntactical. You can make vars private by not including them in the returned object.

angular.module(‘myApp’, [])

.factory(‘myFactory’, function() {

var returnObj = {}; // we’ll return everything in this object and make it public.
var privateVar = ‘private’; // We can use this var without exposing it outside of our factory.

returnObj.publicVar = ‘foo’; // this will be available to any controller that injects ‘myFactory’.

return returnObj;
})

Service

Confusingly named “service”, the service service is at the end of the chain in the provider/factory/service paradigm. Now you are writing directly inside a constructor object that will be called with the new operator the first time it is instantiated in the app lifecycle. Syntactically this means you make things private by not including them in this.

angular.module(‘myApp’, [])

.service(‘myService’, function() {

var privateVar = ‘private’; // We can use this var without exposing it outside of our service.

this.publicVar = ‘foo’; // this will be available to any controller that injects ‘myService’.
})

Value

Values are a simplified service type that return a single var. Granted, this var can become multi-dimensional if it is an object.

A noteworthy difference between a value and a service is in a service you are inside a constructor function, whereas in a value you can simply return the variable type of your choice.

angular.module(‘myApp’, [])

.value(‘stringValue’, ‘foo’)

.value(‘numberValue’, 5)

.value(‘objectValue’, {
foo: ‘bar’
})

Constant

In practice the difference between value and constant is largely “syntactical sugar.” A constant implies that the variable is not going to change over the lifecycle of the app.

There is nothing implicitly in Angular 1.* that will enforce that. It’s basically up to you to keep the syntactical contract alive. The only technical difference is that value is available in the module config method and constant is not. More on that below.

angular.module(‘myApp’, [])

.constant(‘stringValue’, ‘foo’)

.constant(‘numberValue’, 5)

.constant(‘objectValue’, {
foo: ‘bar’
})

Decorator

A decorator can change the values of a provider before it is available to your app.

The provider/decorator relationship is unique in this way among the services. A decorator also can only exist inside an apps config method. A common use case for this would be to configure defaults of a service without touching the provider itself.

All services except constants (and provider) inherit from a provider, and can be decorated.

angular.module(‘myApp’, [])

.value(‘myValue’, ‘foo’)

.config(function($provide) {

$provide.decorator(‘myValue’, function() {

return ‘bar’; // myValue is now ‘bar’
});
})

Configuring and Decorating Services

I have mentioned decorating and providers a few times in this article. This topic get’s confusing quick as you can create a provider service, but at the same time services, factories and values have providers. I’ll try to clear that up a little.

The Module Config Method

The config method is available in any module and is available before your app has init. Config methods are run in the order they are declared in your code. This is the only place you have access to your app before it has initialized.

angular.module(‘myModule’, [])

.config(function() {
// Any code in here will run before the app is init
})

Configuring a Provider

Typically if you’re going to go through the trouble of writing out the full provider syntax, it’s because you want to create explicit methods for configuring your service before an app inits.

You need to inject your provider into the config method with Provider appended to the end of its name. For example, if your provider is called breakfast, you have to inject breakfastProvider into the config method.

angular.module(‘myApp’, [])

.provider(‘breakfast’, function() {

var defaultFood = ‘eggs’;

return {
setDefaultFood: function(food) {
// this method allows you to override defaultFood before the app inits
defaultFood = food;
},
$get: function() {

return {
// this is where defaultFood is made available to your app
defaultFood: defaultFood
};
}
};
})

// append ‘Provider’ to breakfast to inject it into the config method.
.config([‘breakfastProvider’, function(breakfastProvider) {

breakFastProvider.setDefaultFood(‘bacon’);
// when the app inits breakfast.defaultFood will be ‘bacon’
}])

Decorating a Service, Factory or Value

Services, factories and values can also be configured before the app inits. The syntax for achieving this is a little different.

We still execute our code in the config method, but we have to inject $provide and use the $provide.decorator() method. This is called (you guessed it) decorating. The callback inside the decorator method gives you the $delegate parameter, which contains the value(s) of your service and can be manipulated before it is returned.

angular.module(‘myApp’, [])

.value(‘myValue’, ‘foo’)

.service(‘myService’, function() {

this.foo = ‘foo’;
this.bar = ‘bar’;
})

.factory(‘myFactory’, function() {

return {
foo: ‘foo’,
bar: ‘bar’
};
})

.config(function($provide) {

$provide.decorator(‘myValue’, function($delegate) {

return $delegate + ‘bar’;
// returns ‘foobar’
});

$provide.decorator(‘myService’, function($delegate) {

$delegate.foo = ‘FOO’;
$delegate.baz = ‘baz’;

return $delegate;
// returns {foo: ‘FOO’, bar: ‘bar’, baz: ‘baz’}
});

$provide.decorator(‘myFactory’, function($delegate) {

$delegate.foo = ‘FOO’;
$delegate.myBool = true;

return $delegate;
// returns {foo: ‘FOO’, bar: ‘bar’, myBool: true}
});
})

Purposes of Services

Services are only tied to a few rules. They’re dependency injected, they are lazily instantiated and they are singletons.

Beyond that, and the individual types quirks, it’s up to you to figure out how and why to use them. Below are some common patterns.

Service as a Model

Since services are singletons the same instance of your service will be shared across multiple controllers.

In addition, if one controller is destroyed, and later in the life of your app another is created, the values will remain the same. This makes services a great place to store data you want to persist for the lifecycle of your app. The only service I would not use as a model is constant.

Using a Value as a Model

An easy way to store a persistent data model is to make a value an object literal.

angular.module(‘myApp’, [])

.value(‘myBreakfastModel’, {
prices: {
eggs: 2.50,
coffee: 3.25
},
quantity: {
eggs: 0,
coffee: 0
}
})

Using a Service/Factory/Provider as a Model

Using service, factory and provider gives you some more firepower, allowing you to include methods and private vars easily. In the below example I’ll use a factory, but this could apply to service and provider as well.

angular.module(‘myApp’, [])

.factory(‘myBreakfastModel’, [function() {

var returnObj = {}; // everything in this object will be public
var tax = 0.08; // this var will remain private

returnObj.prices = {
eggs: 2.50,
coffee: 3.25
};
returnObj.quantity = {
eggs: 0,
coffee: 0
};
returnObj.getPrice = function() {

var price = 0;
price += returnObj.prices.eggs * returnObj.quantity.eggs;
price += returnObj.prices.coffee * returnObj.quantity.coffee;
price += (price * tax);

return price;
};

return returnObj;
}])

Service as a Promise

Services can be a nice way to encapsulate promises. If multiple controllers/services need the same data this is an easy way to be sure you only call for it once.

angular.module(‘myApp’, [])

.factory(‘myPromise’,
[‘$http’, function($http,) {

var promise = $http.get(‘/some-url’);

return promise;
}])

.controller(‘myController’,
[‘$scope’, ‘myPromise’,
function($scope, myPromise) {

myPromise.then(function(res) {

$scope.data = res.data;
});
}])

Service as a Constructor

Factories can be used as a way to share constructors across controllers. This is a bit different from how the service service is a constructor, in that the service service will automatically call the new operator and deliver the service service as a singleton across controllers.

Creating a constructor inside a factory allows you to use the new operator in your controller to generate new instances of it in controllers.

angular.module(‘myApp’, [])

.factory(‘myBreakfastConstructor’, [function() {

var BreakfastConstructor = function(eggNum, coffeeNum) {

this.eggNum = eggNum;
this.coffeeNum = coffeeNum;
};
BreakfastConstructor.prototype = {

};

return BreakfastConstructor;
}])

.controller(‘myController’,
[‘$scope’, ‘myBreakfastConstructor’,
function($scope, myBreakfastConstructor) {

$scope.breakfast1 = new myBreakfastConstructor(3, 1);
$scope.breakfast2 = new myBreakfastConstructor(2, 2);
}])

Service as a Constant

Services are a great place to share constants across your app. Whether it is a single constant, or an object literal with many constants.

Though there is a constant service type, you could presumably store them in any service dependent on your love of semantics (but seriously, don’t do that).

angular.module(‘myApp’, [])

.constant(‘myConstant’, ‘foo’)

.constant(‘myConstantsObj’, {
STR_FOO: ‘foo’,
STR_BAR: ‘bar’,
NUM_FIFTEEN: 15
})

Naming Services Semantically

I’m of the belief that if you want code you can share across your app, a singleton, or code that is lazily instantiated, services are a great way to keep things organized.

There is no incorrect use of a service so long as it fits into at least one of these criteria.

Using services for different purposes gets confusing when you are relying on the service type to dictate what it’s purpose is.

If you use the constant service for constants it’s great, but what if sometimes a factory is a promise and other times a factory is a constructor? Using some variation of the word factory as a prefix or suffix wont suffice, i.e. factBreakfast or breakfastFact.

Once I’ve injected the breakfast factory I don’t know if I am handling the then() method of a promise or if I have to use the new operator to create a new instance.

I would suggest naming your services according to what they do rather then what they are. This could be the prefix/suffix method, or whatever makes sense to you, so long as it’s consistent.

Examples

model

angular.module(‘myApp’, [])

.value(‘breakfastModel’, {
prices: {
eggs: 2.50,
coffee: 3.25
},
quantity: {
eggs: 0,
coffee: 0
}
})

promise

angular.module(‘myApp’, [])

.service(‘breakfastPromise’, [‘$http’, function($http) {

this.promise = $http.get(‘/breakfast’);
}])

constructor

angular.module(‘myApp’, [])

.factory(‘breakfastConstructor’, function() {

var Breakfast = function(eggNum, coffeeNum) {

this.eggNum = eggNum;
this.coffeeNum = coffeeNum;
};
Breakfast.prototype = { … };

return Breakfast;
})

constants

angular.module(‘myApp’, [])

.constant(‘breakfastConstants’, {
NUM_EGGS: 3,
NUM_COFFEE: 1,
STR_EGGS: ‘country fresh eggs`,
STR_COFFEE: ‘
roasted Brazilian coffee
})

Unlock commercial real estate insights and opportunities with ease Start Searching

Related Posts