Angular之Providers (Value, Factory, Service and Constant )
Each web application you build is composed of objects that collaborate to get stuff done.(每一个web应用都是由一些对象“组装”成的,这些对象共同合作,来完成特定的任务)These objects need to be instantiated and wired together for the app to work.(这些对象需要被实例化,然后“组装”在一起来使web应用能够工作) In Angular apps most of these objects are instantiated and wired together automatically(自动地) by the injector service.
The injector creates two types of objects, services and specialized objects.(两种对象:1 服务 和 2 特定的对象(控制器,指令,过滤器,动画))
Services are objects whose API is defined by the developer writing the service.
Specialized objects conform to a specific Angular framework API. These objects are one of controllers, directives, filters or animations.
The injector needs to know how to create these objects. You tell it by registering(通过注册) a "recipe" for creating your object with the injector. There are five recipe types.
The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four recipe types — Value, Factory, Service and Constant — are just syntactic sugar(value factory service constant仅仅是依靠在provider上的语法糖) on top of a provider recipe.
Let's take a look at the different scenarios for creating and using services via various recipe types. We'll start with the simplest case possible where various places in your code need a shared string and we'll accomplish this via Value recipe.
Note: A Word on Modules
In order for the injector to know how to create and wire together all of these objects, it needs a registry(注册表) of "recipes". Each recipe has an identifier of the object and the description of how to create this object.
Each recipe belongs to an Angular module(属于一个Angular模块). An Angular module is a bag that holds one or more recipes. And since manually keeping track of module dependencies is no fun, a module can contain information about dependencies on other modules as well.
When an Angular application starts with a given application module, Angular creates a new instance of injector(注册器服务被实例化之后), which in turn creates a registry of recipes as a union of all recipes defined in the core "ng" module, application module and its dependencies(核心的“ng”模块,应用模块和它的依赖). The injector then consults the recipe registry when it needs to create an object for your application.(当需要为你的应用生成一个对象时,注册器就会去“咨询”注册表)
Value Factory Service Provider
Value Recipe
Let's say that we want to have a very simple service called "clientId" that provides a string representing an authentication id used for some remote API. You would define it like this:
var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');
Notice how we created an Angular module called myApp
, and specified that this module definition contains a "recipe" for constructing theclientId
service, which is a simple string in this case.
And this is how you would display it via Angular's data-binding:
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
this.clientId = clientId;
}]);
<html ng-app="myApp">
<body ng-controller="DemoController as demo">
Client ID: {{demo.clientId}}
</body>
</html>
In this example, we've used the Value recipe to define the value to provide when DemoController
asks for the service(当控制器需要这个服务时,“我们”就会提供这个服务) with id "clientId".
On to more complex examples!
Constant Recipe
We've just learned how AngularJS splits the life-cycle into configuration phase and run phase(angular将生命周期分为configuration阶段和run阶段) and how you can provide configuration to your application via the config function. Since the config function runs in the configuration phase when no services are available, it doesn't have access even to simple value objects(constant在configuration阶段就已经有了,而value,service,factory在configuration阶段没有) created via the Value recipe.
Since simple values, like URL prefixes, don't have dependencies or configuration, it's often handy to make them available in both the configuration and run phases. This is what the Constant recipe is for.
Factory Recipe
The Value recipe is very simple to write, but lacks some important features we often need when creating services. Let's now look at the Value recipe's more powerful sibling, the Factory. The Factory recipe adds the following abilities:
- ability to use other services (have dependencies)(可以使用依赖)
- service initialization
- delayed/lazy initialization
The Factory recipe constructs a new service using a function with zero or more arguments (these are dependencies on other services). The return value of this function is the service instance created by this recipe.
Note: All services in Angular are singletons(单列的). That means that the injector uses each recipe at most once to create the object. The injector then caches the reference(缓存起来) for all future needs.
Since a Factory is a more powerful version of the Value recipe, the same service can be constructed with it. Using our previous clientId
Value recipe example, we can rewrite it as a Factory recipe like this:
myApp.factory('clientId', function clientIdFactory() {
return 'a12345654321x';
});
But given that the token is just a string literal, sticking with the Value recipe is still more appropriate as it makes the code easier to follow.
Let's say, however, that we would also like to create a service that computes a token used for authentication against a remote API. This token will be called apiToken
and will be computed based on the clientId
value and a secret stored in the browser's local storage:
myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
var encrypt = function(data1, data2) {
// NSA-proof encryption algorithm:
return (data1 + ':' + data2).toUpperCase();
};
var secret = window.localStorage.getItem('myApp.secret');
var apiToken = encrypt(clientId, secret);
return apiToken;
}]);
In the code above, we see how the apiToken
service is defined via the Factory recipe that depends on the clientId
service. The factory service then uses NSA-proof encryption to produce an authentication token.
<serviceId>Factory
(e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase or looking at stack traces in the debugger.Just like with the Value recipe, the Factory recipe can create a service of any type(Factory可以生成任何类型的service,基本类型,对象字面量,函数,甚至是自定义), whether it be a primitive, object literal, function, or even an instance of a custom type.
与service的区别:
the Service recipe works better for objects of a custom type, while the Factory can produce(相比较service,factory生成的数据类型更多) JavaScript primitives and functions.
Service Recipe
JavaScript developers often use custom types to write object-oriented code. Let's explore how we could launch a unicorn into space via our unicornLauncher
service which is an instance of a custom type:
function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
}
We are now ready to launch unicorns, but notice that UnicornLauncher depends on our apiToken
. We can satisfy this dependency onapiToken
using the Factory recipe:
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
return new UnicornLauncher(apiToken);
}]);
This is, however, exactly the use-case that the Service recipe is the most suitable for.
The Service recipe produces a service just like the Value or Factory recipes, but it does so by invoking a constructor with the new
operator(通过调用一个构造函数). The constructor can take zero or more arguments, which represent dependencies needed by the instance of this type.
Note: Service recipes follow a design pattern called constructor injection.
Since we already have a constructor for our UnicornLauncher type, we can replace the Factory recipe above with a Service recipe like this:
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll be somehow punished for our misdeed. It's like we named one of our offspring 'Child'. Boy, that would mess with the teachers.
Provider Recipe
As already mentioned in the intro, the Provider recipe is the core recipe type(是核心的“服务”类型) and all the other recipe types are just syntactic sugar on top of it(其它的service类型都是以provider为基础的语法糖). It is the most verbose recipe with the most abilities, but for most services it's overkill.
The Provider recipe is syntactically defined as a custom type that implements a $get
method. This method is a factory function just like the one we use in the Factory recipe. In fact, if you define a Factory recipe, an empty Provider type with the $get
method set to your factory function is automatically created under the hood.
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Let's say that our unicornLauncher
service is so awesome that many apps use it. By default the launcher shoots unicorns into space without any protective shielding. But on some planets the atmosphere is so thick that we must wrap every unicorn in tinfoil before sending it on its intergalactic trip, otherwise they would burn while passing through the atmosphere. It would then be great if we could configure the launcher to use the tinfoil shielding for each launch in apps that need it. We can make it configurable like so:
myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
var useTinfoilShielding = false;
this.useTinfoilShielding = function(value) {
useTinfoilShielding = !!value;
};
this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {
// let's assume that the UnicornLauncher constructor was also changed to
// accept and use the useTinfoilShielding argument
return new UnicornLauncher(apiToken, useTinfoilShielding);
}];
});
To turn the tinfoil shielding on in our app, we need to create a config function via the module API and have the UnicornLauncherProvider injected into it:
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
unicornLauncherProvider.useTinfoilShielding(true);
}]);
Notice that the unicorn provider is injected into the config function. This injection is done by a provider injector which is different from the regular instance injector, in that it instantiates and wires (injects) all provider instances only.
During application bootstrap, before Angular goes off creating all services, it configures and instantiates all providers. We call this the configuration phase of the application life-cycle. During this phase, services aren't accessible because they haven't been created yet.
Once the configuration phase is over, interaction with providers is disallowed and the process of creating services starts. We call this part of the application life-cycle the run phase.
--------------------------------------------------------------------------------------
Confused about Service vs Factory
All angular services are singletons(angular中所有的服务都是单例的,只被实例化一次):
All Services are singletons; they get instantiated once per app. They can be of any type, whether it be a primitive, object literal, function, or even an instance of a custom type.
For me the revelation came when I realise that they all work the same way: by running something once, storing the value they get, and then cough up that same stored value when referenced through Dependency Injection.(实例化一次,依赖注入时注入的都是同一个服务)
Say we have:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
The difference between the three is that:
a's stored value comes from running fn
b’s stored value comes from newing fn
c’s stored value comes from first getting an instance by newing fn, and then running a $get method of the instance
which means, there’s something like a cache object inside angular, whose value of each injection is only assigned once, when they've been injected the first time, and where:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
This is why we use this in services, and define a this.$get in providers(这就是为什么在service中使用this 在provider中使用this.$get方法).
---------------------------------------------------------------------------------------
AngularJS: Service vs provider vs factory
Services
Syntax: module.service( 'serviceName', function );
Result: When declaring serviceName as an injectable argument you will be provided with an instance of the function. In other words new FunctionYouPassedToService()
.
当使用service进行注入时,你会得到函数的实例,换句话说,即使用了new关键字。
Factories
Syntax: module.factory( 'factoryName', function );
Result: When declaring factoryName as an injectable argument you will be provided with the value that is returned(得到的是调用函数返回的东西) by invoking the function reference passed to module.factory.
Providers
Syntax: module.provider( 'providerName', function );
Result: When declaring providerName as an injectable argument you will be provided with (new ProviderFunction()).$get()
. The constructor function is instantiated before the $get method is called(调用$get方法之前,会调用constructor函数,因此,可以在configuration阶段进行配置) - ProviderFunction
is the function reference passed to module.provider.
Providers have the advantage that they can be configured(provider有一个优势,在使用之前,可以被配置) during the module configuration phase.
下面是使用几种不同服务之间的区别,和为什么会有几种服务的原因.
Here's a great further explanation by Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
In this case the injector simply returns the value as is. But what if you want to compute the value(如果你想要计算这个值呢)? Then use a factory
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
So factory
is a function which is responsible for creating the value. Notice that the factory function can ask for other dependencies.
But what if you want to be more OO and have a class called Greeter(如果你想更面向对象,有一个“类”呢)?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
Then to instantiate you would have to write
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Then we could ask for 'greeter' in controller like this
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
But that is way too wordy. A shorter way to write this would be provider.service('greeter', Greeter);
But what if we wanted to configure the Greeter
class before the injection(如果注入之前想要进行配置呢)? Then we could write
provide.provider('greeter2', function() {
var salutation = 'Hello';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + ' ' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
Then we can do this:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
As a side note, service
, factory
, and value(这三者其实都是依据在provider之上的)
are all derived from provider.
provider.service = function(name, Class) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.instantiate(Class);
};
});
}
provider.factory = function(name, factory) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.invoke(factory);
};
});
}
provider.value = function(name, value) {
provider.factory(name, function() {
return value;
});
};
Angular之Providers (Value, Factory, Service and Constant )的更多相关文章
- Angular1.x 之Providers (Value, Factory, Service and Constant )
官方文档Providers Each web application you build is composed of objects that collaborate to get stuff do ...
- [译]AngularJS中几种Providers(Factory, Service, Provider)的区别
原文: http://blog.xebia.com/2013/09/01/differences-between-providers-in-angularjs/ 什么是Provider? Angula ...
- 我也谈“the difference between Factory, Service, and Provider in Angular”
看完这篇文章之后的理解与实践:原文地址:http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/ <!doctype ...
- AngularJS 服务 provider factory service及区别
一.概念说明 1.服务是对公共代码的抽象,如多个控制器都出现了相似代码,把他们抽取出来,封装成一个服务,遵循DRY原则,增强可维护性,剥离了和具体表现相关的部分,聚焦于业务逻辑或交互逻辑,更加容易被测 ...
- AngularJS 讲解五, Factory ,Service , Provider
一. 首先说一下,为什么要引入Factory,Service和Provider这三个Service层. 1.因为我们不应该在controller层写入大量的业务逻辑和持久化数据,controller层 ...
- factory service provide自定义服务
1.factory factory , 就是你提供一个方法, 该方法返回一个对象的实例, 对于 AngularJS 的 factory 来说, 就是先定义一个对象, 给这个对象添加属性和方法, 然后返 ...
- angularjs factory,service,provider 自定义服务的不同
angularjs框架学了有一段时间了,感觉很好用.可以把angularjs的app理解成php的class,controller是控制器,而内置服务和自定义服务就可以理解成models了.angul ...
- AngularJS Factory Service Provider
先看看http://www.cnblogs.com/mbydzyr/p/3460501.html http://www.oschina.net/translate/angularjs-factory- ...
- angularjs中factory, service和provider
在Angular里面,services作为单例对象在需要到的时候被创建,只有在应用生命周期结束的时候(关闭浏览器)才会被清除.而controllers在不需要的时候就会被销毁了(因为service的底 ...
随机推荐
- 让C#、VB.NET实现复杂的二进制操作
VB.NET和C#属于高级语言,对二进制位操作的支持不是很好,比如没有了移位运算等,用的时候确实很不方便,所以在闲暇之余我重新封装了一个用于C#.VB.NET的位操作类库,通过该类库可以实现数据移位. ...
- IOS开发笔记 - 基于SDWebImage的网络图片加载处理
前言: 在IOS下通过URL读一张网络图片并不像Asp.net那样可以直接把图片路径放到图片路径的位置就ok, 而是需要我们通过一段类似流的方式去加载网络图片,接着才能把图片放入图片路径显示. 这里找 ...
- phper談談最近重構代碼的感受(1)
作爲一個工作時間並不算長的phper,卻參與了兩家公司的代碼重構.下面談談我的一些感受. 在mjm公司,當時我負責日常的需求開發和2.0的重構.當初的重構更多的是clean codes和一些代碼規範上 ...
- java nio io模型
I/O模型 在开始NIO的学习之前,先对I/O的模型有一个理解,这对NIO的学习是绝对有好处的.我画一张图,简单表示一下数据从外部磁盘向运行中进程的内存区域移动的过程: 这张图片明显忽略了很多细节,只 ...
- jsp-1 简单的应用servlet,并用其跳转页面
jspweb里面用到的servlet跳转页面的方法 使用的jar包只有 commons-lang3-3.5.jar 运行时,tomcat会先根据web.xml里面的信息,查找servlet <? ...
- 【转】经典!python中使用xlrd、xlwt操作excel表格详解
最近遇到一个情景,就是定期生成并发送服务器使用情况报表,按照不同维度统计,涉及python对excel的操作,上网搜罗了一番,大多大同小异,而且不太能满足需求,不过经过一番对源码的"研究&q ...
- 2016 ACM/ICPC Asia Regional Qingdao Online 1002 Cure
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...
- keychain的使用
通常情况下,我们用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码.证书等等,就需要使用更为安全的keychain了.keychain里保存的信息不会因App被删除而丢失,在用 ...
- delphi用TAdoStoredProc调用存储过程,兼容sql2005、2008、2014的远程事务问题
delphi7写的程序,在sql2000里没问题,调用sql2008.2014里的存储过程时,如果存储过程里操作了大量数据,很容易会莫名其妙的自己撤销掉,但是程序还识别不到,认为还在正常执行.今天尝试 ...
- python爬虫框架scrapy初试(二)
将该导航网站搜索出结果的页面http://www.dmoz.org/Computers/Programming/Languages/Python/Books/里面标题,及标题的超链接和描述爬下来. 使 ...