Last update: June 2014. I have partially rewritten this article to provide more technical details and also to show their differences more clearly.


Angular comes with different types of services. Each one with its own use cases.

Something important that you have to keep in mind is that the services are always singleton, it doesn’t matter which type you use. This is the desired behavior.

NOTE: A singleton is a design pattern that restricts the instantiation of a class to just one object. Every place where we inject our service, will use the same instance.

Provider

Provider is the parent of almost all the other services (all but constant) and it is also the most complex but more configurable one.

Let’s see a basic example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
app.provider('foo', function() {

  return {

    $get: function() {
var thisIsPrivate = "Private";
function getPrivate() {
return thisIsPrivate;
} return {
variable: "This is public",
getPrivate: getPrivate
};
} }; });

provider on its simplest form, just needs to return a function called $get which is what we inject on the other components. So if we have a controller and we want to inject this foo provider, what we inject is the $get function of it.

Why should we use a provider when a factory is much simple? Because we can configure a provider in the config function. We can do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
app.provider('foo', function() {

  var thisIsPrivate = "Private";

  return {

    setPrivate: function(newVal) {
thisIsPrivate = newVal;
}, $get: function() {
function getPrivate() {
return thisIsPrivate;
} return {
variable: "This is public",
getPrivate: getPrivate
};
} }; }); app.config(function(fooProvider) {
fooProvider.setPrivate('New value from config');
});

Here we moved the thisIsPrivate outside our $get function and then we created a setPrivate function to be able to change thisIsPrivate in a config function. Why do we need to do this? Won’t it be easier to just add the setter in the $get? This has a different purpose.

Imagine we want to create a generic library to manage our models and make some REST petitions. If we hardcode the endpoints URLs, we are not making it any generic, so the idea is to be able to configure those URLs and to do so, we create a provider and we allow those URLs to be configured on a config function.

Notice that we have to put nameProvider instead of just name in our config function. To consume it, we just need to use name.

Seeing this we realize that we already configured some services in our applications, like $routeProvider and $locationProvider, to configure our routes and html5mode respectively.

Providers have two different places to make injections, on the provider constructor and on the $get function. On the provider constructor we can only inject other providers and constants (is the same limitation as the config function). On the $get function we can inject all but other providers (but we can inject other provider’s $get function).

Remember: To inject a provider you use: name + ‘Provider’ and to inject its $get function you just use name

Try it


Factory

Provider are good, they are quite flexible and complex. But what if we only want its $getfunction? I mean, no configuration at all. Well, in that cases we have the factory. Let’s see an example:

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.factory('foo', function() {
var thisIsPrivate = "Private";
function getPrivate() {
return thisIsPrivate;
} return {
variable: "This is public",
getPrivate: getPrivate
};
}); // or.. app.factory('bar', function(a) {
return a * 2;
});

As you see, we moved our provider $get function into a factory so we have what we had on the first provider example but with a much simpler syntax. In fact, internally a factory is a provider with only the $get function.

As I said before, all types are singleton, so if we modify foo.variable in one place, the other places will have that change too.

We can inject everything but providers on a factory and we can inject it everywhere except on the provider constructor and config functions.

Try it


Value

Factory is good, but what if I just want to store a simple value? I mean, no injections, just a simple value or object. Well angular has you covered with the value service:

Example:

1
app.value('foo', 'A simple value');

Internally a value is just a factory. And since it is a factory the same injection rules applies, AKA can’t be injected into provider constructor or config functions.

Try it


Service

So having the complex provider, the more simple factory and the value services, what is the service service? Let’s see an example first:

Example:

1
2
3
4
5
6
7
app.service('foo', function() {
var thisIsPrivate = "Private";
this.variable = "This is public";
this.getPrivate = function() {
return thisIsPrivate;
};
});

The service service works much the same as the factory one. The difference is simple: The factory receives a function that gets called when we create it and the servicereceives a constructor function where we do a new on it (actually internally is uses Object.create instead of new).

In fact, it is the same thing as doing this:

1
2
3
4
5
6
7
8
9
10
11
12
app.factory('foo2', function() {
return new Foobar();
}); function Foobar() {
var thisIsPrivate = "Private";
this.variable = "This is public";
this.getPrivate = function() {
return thisIsPrivate;
};
}

Foobar is a constructor function and we instantiate it in our factory when angular processes it for the first time and then it returns it. Like the service, Foobar will be instantiated only once and the next time we use the factory it will return the same instance again.

If we already have the class and we want to use it in our service we can do that like the following:

1
app.service('foo3', Foobar);

If you’re wondering, what we did on foo2 is actually what angular does with services internally. That means that service is actually a factory and because of that, same injection rules applies.

Try it


Constant

Then, you’re expecting me to say that a constant is another subtype of provider like the others, but this one is not. A constant works much the same as a value as we can see here:

Example:

1
2
3
4
app.constant('fooConfig', {
config1: true,
config2: "Default config2"
});

So… what’s the difference then? A constant can be injected everywhere and that includes provider constructor and config functions. That is why we use constant services to create default configuration for directives, because we can modify those configuration on our config functions.

You are wondering why it is called constant if we can modify it and well that was a design decision and I have no idea about the reasons behind it.

Try it


Bonus 1: Decorator

So you decided that the foo service I sent to you lacks a greet function and you want it. Will you modify the factory? No! You can decorate it:

1
2
3
4
5
6
7
8
9
app.config(function($provide) {
$provide.decorator('foo', function($delegate) {
$delegate.greet = function() {
return "Hello, I am a new function of 'foo'";
}; return $delegate;
});
});

$provide is what Angular uses internally to create all the services. We can use it to create new services if we want but also to decorate existing services. $provide has a method called decorator that allows us to do that. decorator receives the name of the service and a callback function that receives a $delegate parameter. That $delegate parameter is our original service instance.

Here we can do what we want to decorate our service. In our case, we added a greetfunction to our original service. Then we return the new modified service.

Now when we consume it, it will have the new greet function as you will see in the Try it.

The ability to decorate services comes in handy when we are consuming 3rd party services and we want to decorate it without having to copy it in our project and then doing there the modifications.

Note: The constant service cannot be decorated.

Try it


Bonus 2: Creating new instances

Our services are singleton but we can create a singleton factory that creates new instances. Before you dive deeper, keep in mind that having singleton services is the way to go and we don’t want to change that. Said that, in the rare cases you need to generate new instances, you can do that like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Our class
function Person( json ) {
angular.extend(this, json);
} Person.prototype = {
update: function() {
// Update it (With real code :P)
this.name = "Dave";
this.country = "Canada";
}
}; Person.getById = function( id ) {
// Do something to fetch a Person by the id
return new Person({
name: "Jesus",
country: "Spain"
});
}; // Our factory
app.factory('personService', function() {
return {
getById: Person.getById
};
});

Here we create a Person object which receives some json to initialize the object. Then we created a function in our prototype (functions in the prototype are for the instances of the Person) and a function directly in Person (which is like a class function).

So we have a class function that will create a new Person object based on the id that we provide (well, it will in real code) and every instance is able to update itself. Now we just need to create a service that will use it.

Every time we call personService.getById we are creating a new Person object, so you can use this service in different controllers and even when the factory in a singleton, it generates new objects.

Kudos to Josh David Miller for his example.

Try it


Bonus 3: Coffeescript

Coffeescript can be handy with services since they provide a prettier way to create classes. Let’s see an example of the Bonus 2 using Coffeescript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
app.controller 'MainCtrl', ($scope, personService) ->
$scope.aPerson = personService.getById(1) app.controller 'SecondCtrl', ($scope, personService) ->
$scope.aPerson = personService.getById(2)
$scope.updateIt = () ->
$scope.aPerson.update() class Person constructor: (json) ->
angular.extend @, json update: () ->
@name = "Dave"
@country = "Canada" @getById: (id) ->
new Person
name: "Jesus"
country: "Spain" app.factory 'personService', () ->
{
getById: Person.getById
}

It is prettier now in my humble opinion.

Try it


NOTE: This last one, being Coffeescript seems to fail a little bit with JSbin. Go to the Javascript tab and select Coffeescript to make it work.

Conclusion

Services are one of the coolest features of Angular. We have a lot of ways to create them, we just need to pick the correct one for our use cases and implement it.

If you found any issue or you think that this can be improved, please leave an issue or pull request at Github. In any case, a comment will be appreciated :).

http://angular-tips.com/blog/2013/08/understanding-service-types/

Understanding Service Types的更多相关文章

  1. 浅析Kubernrtes服务类型(Service Types)

    先上图 在Kubernetes集群中,service通过标签选择器选着对应的pod,然后对请求进行转发,看个动画,能直接了当体会到便签选择器 pod,endpoints,service三者关系 1.举 ...

  2. Learning WCF Chapter1 Generating a Service and Client Proxy

    In the previous lab,you created a service and client from scratch without leveraging the tools avail ...

  3. Learning WCF Chapter1 Exposing Multiple Service Endpoints

    So far in this chapter,I have shown you different ways to create services,how to expose a service en ...

  4. Service Fabric Cluster Manager

    作者:潘罡 (Van Pan)@ Microsoft 我们回到Service Fabric最底层的话题,谈谈Service Fabric是怎么工作的. 首先,我们回到下面的文档,看看Service F ...

  5. service fabric docker 安装

    1. 镜像拉取 docker pull microsoft/service-fabric-onebox 2. 配置docker(daemon.json) { "ipv6": tru ...

  6. F#之旅2 - 我有特别的学F#技巧

    原文地址:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/learning-fsharp/ Learning F#Functio ...

  7. WCF服务与WCF数据服务的区别

    问: Hi, I am newbie to wcf programming and a little bit confused between WCF Service and WCF Data  Se ...

  8. wcf中的File-less Activation

    File-less Activation Although .svc files make it easy to expose WCF services, an even easier approac ...

  9. Eclipse MAT: Understand Incoming and Outgoing References

    引用:http://xmlandmore.blogspot.hk/2014/01/eclipse-mat-understand-incoming-and.html?utm_source=tuicool ...

随机推荐

  1. spring整合freemarker 自定义标签

    1.自定义标签实现 TemplateDirectiveModel 接口 2.spring 配置,注意标红的两行 <bean id="freemarkerConfig" cla ...

  2. ps 命令详解

    有时候系统管理员可能只关心现在系统中运行着哪些程序,而不想知道有哪些进程在运行.由于一个应用程序可能需要启动多个进程.所以在同等情况下,进程的数 量要比程序多的多.为此从阅读方面考虑,管理员需要知道系 ...

  3. Vim中的正则表达式[转]

    来自:http://blog.csdn.net/endall/archive/2007/08/29/1764554.aspx Vim中的正则表达式功能很强大,如果能自由运用,则可以完成很多难以想象的操 ...

  4. Nginx 下无法读取session 导致 thinkphp验证码错误

    打开php配置文件 php.ini 使用搜索命令 whereis php.ini 一般在:/etc/php.ini 目录下 使用vim命令打开 找到: session.save_path 找到php保 ...

  5. 简单linux块设备驱动程序

    本文代码参考<LINUX设备驱动程序>第十六章 块设备驱动程序 本文中的“块设备”是一段大小为PAGE_SIZE的内存空间(两个扇区,每个扇区512字节) 功能:向块设备中输入内容,从块设 ...

  6. 关于fork函数中的内存复制和共享

    原来刚刚开始做linux下面的多进程编程的时候,对于下面这段代码感到很奇怪, #include<unistd.h> #include<stdio.h> #include< ...

  7. bootstrap知识小点

    年底没什么项目做了,整理下最近做的网站使用到的bootstrap知识 一.导入bootstrap样式和脚本 <link href="css/bootstrap.min.css" ...

  8. c#中执行多条sql语句【ORA-00911: 无效字符】

    问题描述: 在plsql里执行多条sql语句的时候,使用“,”(逗号)分隔,测试可以执行多条,而在C#执行多条sql语句的时候[ORA-00911: 无效字符]. 有时我们需要一次性执行多条sql语句 ...

  9. asp 301跳转代码

    <%    Response.Status="301 Moved Permanently"     Response.AddHeader "Location&quo ...

  10. thinkphp 字段静态验证$_validate中错误提醒多语言化写成{%LANGUATE}的原因

    class UserModel extends Model{ protected $_validate =  array( array('account', 'require', '{%LANGUAG ...