AngularJS:服务vs提供者vs工厂
AngularJS中的Service
, Provider
和Factory
有什么区别?
从AngularJS邮件列表中,我得到了一个令人惊叹的线程 ,解释了服务与工厂VS提供者以及它们的注入使用情况。 编译答案:
服务
语法: module.service( 'serviceName', function );
结果:当将serviceName声明为注入参数时,将提供该函数的一个实例。 换句话说, new FunctionYouPassedToService()
。
工厂
语法: module.factory( 'factoryName', function );
结果:当将factoryName声明为注入参数时,将提供通过调用传递给module.factory的函数引用返回的值 。
供应商
语法: module.provider( 'providerName', function );
结果:当将providerName声明为注入参数时,将提供 (new ProviderFunction()).$get()
。 构造函数在调用$ get方法之前实例化 – ProviderFunction
是传递给module.provider的函数引用。
提供者具有可以在模块configuration阶段configuration的优点。
看到这里提供的代码。
Misko给出了一个很好的解释:
provide.value('a', 123); function Controller(a) { expect(a).toEqual(123); }
在这种情况下,喷油器只是简单地返回值。 但是如果你想计算这个值呢? 然后使用工厂
provide.factory('b', function(a) { return a*2; }); function Controller(b) { expect(b).toEqual(246); }
所以factory
是一个负责创造价值的function。 请注意,工厂函数可以要求其他的依赖关系。
但是如果你想要做更多的面向对象并且有一个叫Greeter的类呢?
function Greeter(a) { this.greet = function() { return 'Hello ' + a; } }
然后实例化你将不得不写
provide.factory('greeter', function(a) { return new Greeter(a); });
然后我们可以像这样在控制器中请求“迎候”
function Controller(greeter) { expect(greeter instanceof Greeter).toBe(true); expect(greeter.greet()).toEqual('Hello 123'); }
但是这太罗嗦了。 写一个更简短的方法是provider.service('greeter', Greeter);
但是如果我们想在注入之前configurationGreeter
类呢? 然后我们可以写
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); }; });
那我们可以这样做:
angular.module('abc', []).config(function(greeter2Provider) { greeter2Provider.setSalutation('Halo'); }); function Controller(greeter2) { expect(greeter2.greet()).toEqual('Halo 123'); }
作为一个侧面说明, service
, factory
和value
都来自提供者。
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; }); };
JS小提琴演示
factory
/ service
/ provider
“Hello world”示例:
var myApp = angular.module('myApp', []); //service style, probably the simplest one myApp.service('helloWorldFromService', function() { this.sayHello = function() { return "Hello, World!"; }; }); //factory style, more involved but more sophisticated myApp.factory('helloWorldFromFactory', function() { return { sayHello: function() { return "Hello, World!"; } }; }); //provider style, full blown, configurable version myApp.provider('helloWorld', function() { this.name = 'Default'; this.$get = function() { var name = this.name; return { sayHello: function() { return "Hello, " + name + "!"; } } }; this.setName = function(name) { this.name = name; }; }); //hey, we can configure a provider! myApp.config(function(helloWorldProvider){ helloWorldProvider.setName('World'); }); function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) { $scope.hellos = [ helloWorld.sayHello(), helloWorldFromFactory.sayHello(), helloWorldFromService.sayHello()]; }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <body ng-app="myApp"> <div ng-controller="MyCtrl"> {{hellos}} </div> </body>
TL; DR
1)当你使用工厂时,你创build一个对象,添加属性,然后返回相同的对象。 当你把这个工厂传入你的控制器时,那个对象上的这些属性现在可以通过你的工厂在那个控制器中使用。
app.controller('myFactoryCtrl', function($scope, myFactory){ $scope.artist = myFactory.getArtist(); }); app.factory('myFactory', function(){ var _artist = 'Shakira'; var service = {}; service.getArtist = function(){ return _artist; } return service; });
2)当你使用Service时 ,AngularJS用“new”关键字在幕后实例化它。 因此,您将添加属性到“this”,服务将返回“this”。 当您将服务传递到您的控制器时,“this”上的这些属性现在将通过您的服务在该控制器上提供。
app.controller('myServiceCtrl', function($scope, myService){ $scope.artist = myService.getArtist(); }); app.service('myService', function(){ var _artist = 'Nelly'; this.getArtist = function(){ return _artist; } });
3) 提供程序是您可以传递到.config()函数的唯一服务。 如果要在提供服务对象之前提供模块范围的configuration,请使用提供程序。
app.controller('myProvider', function($scope, myProvider){ $scope.artist = myProvider.getArtist(); $scope.data.thingFromConfig = myProvider.thingOnConfig; }); app.provider('myProvider', function(){ //Only the next two lines are available in the app.config() this._artist = ''; this.thingFromConfig = ''; this.$get = function(){ var that = this; return { getArtist: function(){ return that._artist; }, thingOnConfig: that.thingFromConfig } } }); app.config(function(myProviderProvider){ myProviderProvider.thingFromConfig = 'This was set in config'; });
非TL; DR
1)工厂
工厂是创build和configuration服务最stream行的方式。 DR说,实际上没有比TL更多的东西。 你只要创build一个对象,给它添加属性,然后返回相同的对象。 然后,当您将工厂传递到您的控制器时,对象上的这些属性现在将通过您的工厂在该控制器中可用。 下面是一个更广泛的例子。
app.factory('myFactory', function(){ var service = {}; return service; });
现在,当我们将“myFactory”传递给我们的控制器时,无论我们附加到“服务”的属性是否可用。
现在让我们在callback函数中添加一些“私有”variables。 这些将不能直接从控制器访问,但是我们最终将在“服务”上设置一些getter / setter方法,以便在需要时能够更改这些“私有”variables。
app.factory('myFactory', function($http, $q){ var service = {}; var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'; return _finalUrl } return service; });
在这里你会注意到我们没有将这些variables/函数附加到“服务”上。 我们只是简单地创build它们,以便稍后使用或修改它们。
- baseUrl是iTunes API所需的基本URL
- _artist是我们希望查找的艺术家
- _finalUrl是我们将调用iTunes的最终完整构build的URL
- makeUrl是一个函数,将创build并返回我们的iTunes友好的URL。
现在我们的助手/私有variables和函数已经到位,让我们添加一些属性到“服务”对象。 无论我们把“服务”放在哪里,都可以直接用在我们传递'myFactory'的控制器里面。
我们将创buildsetArtist并获取艺术家方法,简单地返回或设置艺术家。 我们还将创build一个方法,用我们创build的URL调用iTunes API。 这个方法将返回一个数据从iTunes API返回的诺言。 如果您在AngularJS中没有很多使用承诺的经验,我强烈build议您深入了解它们。
setArtist下面接受一位艺术家,并允许您设置艺术家。 getArtist返回艺术家。 callItunes首先调用makeUrl()以build立我们将用于$ http请求的URL。 然后它设置一个promise对象,使用我们的最终url创build一个$ http请求,然后由于$ http返回一个promise,我们可以在请求后调用.success或.error。 然后,我们用iTunes数据解决我们的承诺,或者我们拒绝一个消息,说“有错误”。
app.factory('myFactory', function($http, $q){ var service = {}; var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK' return _finalUrl; } service.setArtist = function(artist){ _artist = artist; } service.getArtist = function(){ return _artist; } service.callItunes = function(){ makeUrl(); var deferred = $q.defer(); $http({ method: 'JSONP', url: _finalUrl }).success(function(data){ deferred.resolve(data); }).error(function(){ deferred.reject('There was an error') }) return deferred.promise; } return service; });
现在我们的工厂是完整的。 我们现在可以在任何控制器中注入'myFactory',然后我们可以调用我们连接到服务对象(setArtist,getArtist和callItunes)的方法。
app.controller('myFactoryCtrl', function($scope, myFactory){ $scope.data = {}; $scope.updateArtist = function(){ myFactory.setArtist($scope.data.artist); }; $scope.submitArtist = function(){ myFactory.callItunes() .then(function(data){ $scope.data.artistData = data; }, function(data){ alert(data); }) } });
在上面的控制器中,我们正在注入“myFactory”服务。 然后,我们使用“myFactory”中的数据在$ scope对象上设置属性。 上面唯一棘手的代码是,如果你从来没有处理过承诺。 由于callItunes正在返回一个承诺,我们可以使用.then()方法,只要设置$ scope.data.artistData一旦我们的承诺与iTunes数据完成。 你会注意到我们的控制器非常“薄”(这是一个很好的编码习惯)。 我们所有的逻辑和持久性数据都位于我们的服务中,而不是在我们的控制器中。
2)服务
在处理创build服务时最重要的事情可能是使用“new”关键字实例化。 对于你的JavaScript专家来说,这应该给你一个很大的提示,以代码的性质。 对于那些对JavaScript有限背景或对“新”关键字实际上不太熟悉的人,我们来回顾一下JavaScript的基础知识,这些基础知识最终将帮助我们理解服务的本质。
要真正看到用'new'关键字调用一个函数时发生的变化,我们来创build一个函数并用'new'关键字调用它,然后让我们看看解释器在看到'new'关键字时会做些什么。 最终结果将是相同的。
首先让我们创build我们的构造函数。
var Person = function(name, age){ this.name = name; this.age = age; }
这是一个典型的JavaScript构造函数。 现在每当我们使用'new'关键字调用Person函数,'this'将被绑定到新创build的对象。
现在让我们在Person的原型上添加一个方法,这样它就可以在我们Person类的每个实例上使用。
Person.prototype.sayName = function(){ alert('My name is ' + this.name); }
现在,因为我们把sayName函数放在原型上,Person的每个实例都可以调用sayName函数来提醒实例的名字。
现在我们有Person构造函数和我们的sayName函数在它的原型上,让我们实际创build一个Person的实例,然后调用sayName函数。
var tyler = new Person('Tyler', 23); tyler.sayName(); //alerts 'My name is Tyler'
所以一起创build一个Person构造函数的代码,添加一个函数到它的原型,创build一个Person实例,然后调用它的原型上的函数看起来像这样。
var Person = function(name, age){ this.name = name; this.age = age; } Person.prototype.sayName = function(){ alert('My name is ' + this.name); } var tyler = new Person('Tyler', 23); tyler.sayName(); //alerts 'My name is Tyler'
现在让我们看看在JavaScript中使用“new”关键字时发生了什么。 首先你要注意的是,在我们的例子中使用'new'之后,我们可以调用'tyler'上的一个方法(sayName),就像它是一个对象 – 这是因为它是。 所以首先,我们知道我们的Person构造函数正在返回一个对象,无论我们能否在代码中看到它。 其次,我们知道,因为我们的sayName函数位于原型上,而不是直接在Person实例上,所以Person函数返回的对象必须在失败的查找中委托给它的原型。 用更简单的话来说,当我们调用tyler.sayName()时,解释器会说:“好吧,我要看看刚刚创build的'tyler'对象,findsayName函数,然后调用它。 等一下,我在这里看不到 – 我看到的只是名字和年龄,让我检查原型。 是的,看起来好像是在原型上,让我来称呼它。“
以下代码是关于如何考虑“新”关键字在JavaScript中实际执行的操作的代码。 这基本上是上述段落的代码示例。 我已经把“解释器视图”或者解释器看到注释里面的代码的方式。
var Person = function(name, age){ //The below line creates an object(obj) that will delegate to the person's prototype on failed lookups. //var obj = Object.create(Person.prototype); //The line directly below this sets 'this' to the newly created object //this = obj; this.name = name; this.age = age; //return this; }
现在掌握了关于'new'关键字在JavaScript中做什么的知识,在AngularJS中创buildService应该更容易理解。
在创build服务时要理解的最重要的事情是知道服务是用“新”关键字实例化的。 结合这些知识与上面的例子,你现在应该认识到,你将直接附加你的属性和方法到'this',然后将从服务本身返回。 让我们来看看这个在行动。
与我们最初使用Factory示例不同的是,我们不需要创build对象,然后返回该对象,因为像之前多次提到的那样,我们使用“new”关键字,因此解释器将创build该对象,并将其委派给它的原型,然后返回给我们,我们不必做这项工作。
首先,让我们创build我们的“私人”和帮手function。 这应该看起来很熟悉,因为我们和我们的工厂完全一样。 我不会解释每一行在这里做什么,因为我在工厂的例子中做了这个,如果你感到困惑,重新读一下这个工厂的例子。
app.service('myService', function($http, $q){ var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK' return _finalUrl; } });
现在,我们将把我们控制器中可用的所有方法都附加到“this”中。
app.service('myService', function($http, $q){ var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK' return _finalUrl; } this.setArtist = function(artist){ _artist = artist; } this.getArtist = function(){ return _artist; } this.callItunes = function(){ makeUrl(); var deferred = $q.defer(); $http({ method: 'JSONP', url: _finalUrl }).success(function(data){ deferred.resolve(data); }).error(function(){ deferred.reject('There was an error') }) return deferred.promise; } });
现在就像在我们的工厂一样,无论我们将服务传递给哪个控制器,都可以使用setArtist,getArtist和callItunes。 这是myService控制器(几乎和我们的工厂控制器一样)。
app.controller('myServiceCtrl', function($scope, myService){ $scope.data = {}; $scope.updateArtist = function(){ myService.setArtist($scope.data.artist); }; $scope.submitArtist = function(){ myService.callItunes() .then(function(data){ $scope.data.artistData = data; }, function(data){ alert(data); }) } });
就像我之前提到的,一旦你真正理解了“新”的作用,服务几乎和AngularJS中的工厂一样。
3)提供者
关于提供者最值得记住的是他们是唯一可以传递到应用程序的app.config部分的服务。 这是非常重要的,如果你需要改变你的服务对象的任何其他地方可用之前,你的服务对象的一部分。 尽pipe与服务/工厂非常相似,但我们将讨论一些差异。
首先,我们以与我们的服务和工厂类似的方式build立供应商。 下面的variables是我们的“私人”和帮手function。
app.provider('myProvider', function(){ var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; //Going to set this property on the config function below. this.thingFromConfig = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK' return _finalUrl; } }
*再次,如果上述代码的任何部分是令人困惑的,请查看Factory部分,我将解释它是如何做更多细节的。
您可以将提供者视为有三个部分。 第一部分是稍后将被修改/设置的“私有”variables/函数(如上所示)。 第二部分是将在你的app.config函数中可用的variables/函数,因此可以在其他地方可用之前进行更改(也如上所示)。 需要注意的是,这些variables需要附加到“this”关键字。 在我们的例子中,只有'thingFromConfig'可以在app.config中改变。 第三部分(如下所示)是将“myProvider”服务传递到特定控制器时在控制器中可用的所有variables/函数。
在使用Provider创build服务时,控制器中唯一可用的属性/方法是从$ get()函数返回的属性/方法。 下面的代码把$ get放在'this'(我们知道最终将从该函数返回)。 现在,$ get函数返回我们想要在控制器中可用的所有方法/属性。 这是一个代码示例。
this.$get = function($http, $q){ return { callItunes: function(){ makeUrl(); var deferred = $q.defer(); $http({ method: 'JSONP', url: _finalUrl }).success(function(data){ deferred.resolve(data); }).error(function(){ deferred.reject('There was an error') }) return deferred.promise; }, setArtist: function(artist){ _artist = artist; }, getArtist: function(){ return _artist; }, thingOnConfig: this.thingFromConfig } }
现在完整的Provider代码看起来像这样
app.provider('myProvider', function(){ var baseUrl = 'https://itunes.apple.com/search?term='; var _artist = ''; var _finalUrl = ''; //Going to set this property on the config function below this.thingFromConfig = ''; var makeUrl = function(){ _artist = _artist.split(' ').join('+'); _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK' return _finalUrl; } this.$get = function($http, $q){ return { callItunes: function(){ makeUrl(); var deferred = $q.defer(); $http({ method: 'JSONP', url: _finalUrl }).success(function(data){ deferred.resolve(data); }).error(function(){ deferred.reject('There was an error') }) return deferred.promise; }, setArtist: function(artist){ _artist = artist; }, getArtist: function(){ return _artist; }, thingOnConfig: this.thingFromConfig } } });
现在,就像在我们的工厂和服务中一样,setArtist,getArtist和callItunes将会在我们传递myProvider的任何控制器中提供。 这是myProvider控制器(与我们的工厂/服务控制器几乎完全相同)。
app.controller('myProviderCtrl', function($scope, myProvider){ $scope.data = {}; $scope.updateArtist = function(){ myProvider.setArtist($scope.data.artist); }; $scope.submitArtist = function(){ myProvider.callItunes() .then(function(data){ $scope.data.artistData = data; }, function(data){ alert(data); }) } $scope.data.thingFromConfig = myProvider.thingOnConfig; });
如前所述,使用Provider创build服务的关键是在最终对象传递给应用程序的其余部分之前,可以通过app.config函数更改一些variables。 我们来看一个例子。
app.config(function(myProviderProvider){ //Providers are the only service you can pass into app.config myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works'; });
现在你可以看到'thingFromConfig'是如何在我们的提供程序中作为空string,但是当它出现在DOM中时,它将是'This sentence was set …'。
所有服务都是单身人士 ; 他们得到每个应用程序实例化一次。 它们可以是任何types ,不pipe它是原始的,对象的文字,函数,还是一个自定义types的实例。
value
, factory
, service
, constant
和provider
方法都是提供者。 他们教喷油器如何实例化服务。
最详细的,也是最全面的一个是提供者配方。 剩下的四种配方types – 价值,工厂,服务和常量 – 只是提供者配方之上的语法糖 。
- Value Recipe是最简单的情况,您自己实例化Service并将实例化的值提供给注入器。
- 工厂配方为Injector提供了一个工厂函数,当它需要实例化服务时,它会调用它。 被调用时, 工厂函数创build并返回服务实例。 服务的依赖关系作为函数的参数被注入。 所以使用这个配方增加了以下function:
- 能够使用其他服务(具有依赖性)
- 服务初始化
- 延迟/懒惰初始化
- 服务配方几乎与工厂配方相同,但是这里的注入器使用new操作符而不是工厂函数调用构造函数。
- 提供者配方通常是矫枉过正的 。 通过允许您configuration工厂的创build,它增加了一个间接层。
只有当您希望公开一个必须在应用程序启动之前进行的应用程序范围configuration的API时,您应该使用Provider配方。 这通常仅适用于可重用服务,其行为可能需要在应用程序之间略有不同。
常量配方就像价值配方,除了它允许您定义在configuration阶段可用的服务。 比使用Value配方创build的服务更快。 与值不同,它们不能使用 decorator
进行decorator
。请参阅提供者文档 。
了解AngularJS工厂,服务和供应商
所有这些都用于共享可重用的单例对象。 它有助于在应用程序/各种组件/模块之间共享可重用的代码。
从文档服务/工厂 :
- Lazily实例化 – Angular仅在应用程序组件依赖于它时实例化服务/工厂。
- 单例 – 每个依赖于服务的组件获取对由服务工厂生成的单个实例的引用。
厂
工厂是一个function,您可以在创build对象之前操作/添加逻辑,然后返回新创build的对象。
app.factory('MyFactory', function() { var serviceObj = {}; //creating an object with methods/functions or variables serviceObj.myFunction = function() { //TO DO: }; //return that object return serviceObj; });
用法
它可以只是像一个类的function集合。 因此,当你在控制器/工厂/指令function中注入时,它可以在不同的控制器中实例化。 它仅在每个应用程序中实例化一次。
服务
简单的看服务器就可以考虑数组原型。 服务是一个使用“新”关键字实例化新对象的函数。 您可以使用this
关键字将属性和函数添加到服务对象。 与工厂不同,它不返回任何东西(它返回一个包含方法/属性的对象)。
app.service('MyService', function() { //directly binding events to this context this.myServiceFunction = function() { //TO DO: }; });
用法
在整个应用程序中需要共享一个对象时使用它。 例如,authentication的用户详细信息,可共享的方法/数据,实用程序function等
提供商
提供程序用于创build可configuration的服务对象。 您可以从configurationfunctionconfiguration服务设置。 它使用$get()
函数返回一个值。 $get
函数在运行阶段以angular度执行。
app.provider('configurableService', function() { var name = ''; //this method can be be available at configuration time inside app.config. this.setName = function(newName) { name = newName; }; this.$get = function() { var getName = function() { return name; }; return { getName: getName //exposed object to where it gets injected. }; }; });
用法
当你需要为服务对象提供模块化的configuration之前,使其可用,例如。 假设你想根据你的环境设置你的API URL,如dev
, stage
或prod
注意
Only provider will be available in config phase of angular, while service & factory are not.
Hope this has cleared up your understanding about Factory, Service and Provider .
For me, the revelation came when I realized 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 .
假设我们有:
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 runningfn
. -
b
's stored value comes fromnew
ingfn
. -
c
's stored value comes from first getting an instance bynew
ingfn
, and then running a$get
method of the instance.
Which means there's something like a cache object inside AngularJS, 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 vs provider vs factory:
I am trying to keep it simple. It's all about basic JavaScript concept.
First of all, let's talk about services in AngularJS!
What is Service: In AngularJS, Service is nothing but a singleton JavaScript object which can store some useful methods or properties. This singleton object is created per ngApp(Angular app) basis and it is shared among all the controllers within current app. When Angularjs instantiate a service object, it register this service object with a unique service name. So each time when we need service instance, Angular search the registry for this service name, and it returns the reference to service object. Such that we can invoke method, access properties etc on the service object. You may have question whether you can also put properties, methods on scope object of controllers! So why you need service object? Answers is: services are shared among multiple controller scope. If you put some properties/methods in a controller's scope object , it will be available to current scope only. But when you define methods, properties on service object, it will be available globally and can be accessed in any controller's scope by injecting that service.
So if there are three controller scope, let it be controllerA, controllerB and controllerC, all will share same service instance.
<div ng-controller='controllerA'> <!-- controllerA scope --> </div> <div ng-controller='controllerB'> <!-- controllerB scope --> </div> <div ng-controller='controllerC'> <!-- controllerC scope --> </div>
How to create a service?
AngularJS provide different methods to register a service. Here we will concentrate on three methods factory(..),service(..),provider(..);
Use this link for code reference
Factory function:
We can define a factory function as below.
factory('serviceName',function fnFactory(){ return serviceInstance;})
AngularJS provides 'factory('serviceName', fnFactory)' method which takes two parameter, serviceName and a JavaScript function. Angular creates service instance by invoking the function fnFactory() such as below.
var serviceInstace = fnFactory();
The passed function can define a object and return that object. AngularJS simply stores this object reference to a variable which is passed as first argument. Anything which is returned from fnFactory will be bound to serviceInstance . Instead of returning object , we can also return function, values etc, Whatever we will return , will be available to service instance.
例:
var app= angular.module('myApp', []); //creating service using factory method app.factory('factoryPattern',function(){ var data={ 'firstName':'Tom', 'lastName':' Cruise', greet: function(){ console.log('hello!' + this.firstName + this.lastName); } }; //Now all the properties and methods of data object will be available in our service object return data; });
Service Function:
service('serviceName',function fnServiceConstructor(){})
It's the another way, we can register a service. The only difference is the way AngularJS tries to instantiate the service object. This time angular uses 'new' keyword and call the constructor function something like below.
var serviceInstance = new fnServiceConstructor();
In the constructor function we can use 'this' keyword for adding properties/methods to the service object. 例:
//Creating a service using the service method var app= angular.module('myApp', []); app.service('servicePattern',function(){ this.firstName ='James'; this.lastName =' Bond'; this.greet = function(){ console.log('My Name is '+ this.firstName + this.lastName); }; });
Provider function:
Provider() function is the another way for creating services. Let we are interested to create a service which just display some greeting message to the user. But we also want to provide a functionality such that user can set their own greeting message. In technical terms we want to create configurable services. How can we do this ? There must be a way, so that app could pass their custom greeting messages and Angularjs would make it available to factory/constructor function which create our services instance. In such a case provider() function do the job. using provider() function we can create configurable services.
We can create configurable services using provider syntax as given below.
/*step1:define a service */ app.provider('service',function serviceProviderConstructor(){}); /*step2:configure the service */ app.config(function configureService(serviceProvider){});
How does provider syntax internally work?
1.Provider object is created using constructor function we defined in our provider function.
var serviceProvider = new serviceProviderConstructor();
2.The function we passed in app.config(), get executed. This is called config phase, and here we have a chance to customize our service.
configureService(serviceProvider);
3.Finally service instance is created by calling $get method of serviceProvider.
serviceInstance = serviceProvider.$get()
Sample code for creating service using provide syntax:
var app= angular.module('myApp', []); app.provider('providerPattern',function providerConstructor(){ //this function works as constructor function for provider this.firstName = 'Arnold '; this.lastName = ' Schwarzenegger' ; this.greetMessage = ' Welcome, This is default Greeting Message' ; //adding some method which we can call in app.config() function this.setGreetMsg = function(msg){ if(msg){ this.greetMessage = msg ; } }; //We can also add a method which can change firstName and lastName this.$get = function(){ var firstName = this.firstName; var lastName = this.lastName ; var greetMessage = this.greetMessage; var data={ greet: function(){ console.log('hello, ' + firstName + lastName+'! '+ greetMessage); } }; return data ; }; }); app.config( function(providerPatternProvider){ providerPatternProvider.setGreetMsg(' How do you do ?'); } );
Working Demo
概要:
Factory use a factory function which return a service instance. serviceInstance = fnFactory();
Service use a constructor function and Angular invoke this constructor function using 'new' keyword for creating the service instance. serviceInstance = new fnServiceConstructor();
Provider defines a providerConstructor function, this providerConstructor function defines a factory function $get . Angular calls $get() to create the service object. Provider syntax has an added advantage of configuring the service object before it get instantiated. serviceInstance = $get();
As pointed out by several people here correctly a factory, provider, service, and even value and constant are versions of the same thing. You can dissect the more general provider
into all of them. 像这样:
Here's the article this image is from:
厂
You give AngularJS a function, AngularJS will cache and inject the return value when the factory is requested.
例:
app.factory('factory', function() { var name = ''; // Return value **is** the object that will be injected return { name: name; } })
用法:
app.controller('ctrl', function($scope, factory) { $scope.name = factory.name; });
服务
You give AngularJS a function, AngularJS will call new to instantiate it. It is the instance that AngularJS creates that will be cached and injected when the service is requested. Since new was used to instantiate the service, the keyword this is valid and refers to the instance.
例:
app.service('service', function() { var name = ''; this.setName = function(newName) { name = newName; } this.getName = function() { return name; } });
用法:
app.controller('ctrl', function($scope, service) { $scope.name = service.getName(); });
提供商
You give AngularJS a function, and AngularJS will call its $get
function. It is the return value from the $get
function that will be cached and injected when the service is requested.
Providers allow you to configure the provider before AngularJS calls the $get
method to get the injectible.
例:
app.provider('provider', function() { var name = ''; this.setName = function(newName) { name = newName; } this.$get = function() { return { name: name } } })
Usage (as an injectable in a controller)
app.controller('ctrl', function($scope, provider) { $scope.name = provider.name; });
Usage (configuring the provider before $get
is called to create the injectable)
app.config(function(providerProvider) { providerProvider.setName('John'); });
I noticed something interesting when playing around with providers.
Visibility of injectables is different for providers than it is for services and factories. If you declare an AngularJS "constant" (for example, myApp.constant('a', 'Robert');
), you can inject it into services, factories, and providers.
But if you declare an AngularJS "value" (for example., myApp.value('b', {name: 'Jones'});
), you can inject it into services and factories, but NOT into the provider-creating function. You can, however, inject it into the $get
function that you define for your provider. This is mentioned in the AngularJS documentation, but it's easy to miss. You can find it on the %provide page in the sections on the value and constant methods.
<div ng-app="MyAppName"> <div ng-controller="MyCtrl"> <p>from Service: {{servGreet}}</p> <p>from Provider: {{provGreet}}</p> </div> </div> <script> var myApp = angular.module('MyAppName', []); myApp.constant('a', 'Robert'); myApp.value('b', {name: 'Jones'}); myApp.service('greetService', function(a,b) { this.greeter = 'Hi there, ' + a + ' ' + b.name; }); myApp.provider('greetProvider', function(a) { this.firstName = a; this.$get = function(b) { this.lastName = b.name; this.fullName = this.firstName + ' ' + this.lastName; return this; }; }); function MyCtrl($scope, greetService, greetProvider) { $scope.servGreet = greetService.greeter; $scope.provGreet = greetProvider.fullName; } </script>
This is very confusing part for newbie and I have tried to clarify it in easy words
AngularJS Service: is used for sharing utility functions with the service reference in the controller. Service is singleton in nature so for one service only one instance is created in the browser and the same reference is used throughout the page.
In the service, we create function names as property with this object.
AngularJS Factory: the purpose of Factory is also same as Service however in this case we create a new object and add functions as properties of this object and at the end we return this object.
AngularJS Provider: the purpose of this is again same however Provider gives the output of it's $get function.
Defining and using Service, Factory and Provider are explained at http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider
For me the best and the simplest way of understanding the difference is:
var service, factory; service = factory = function(injection) {}
How AngularJS instantiates particular components (simplified):
// service var angularService = new service(injection); // factory var angularFactory = factory(injection);
So, for the service, what becomes the AngularJS component is the object instance of the class which is represented by service declaration function. For the factory, it is the result returned from the factory declaration function. The factory may behave the same as the service:
var factoryAsService = function(injection) { return new function(injection) { // Service content } }
The simplest way of thinking is the following one:
- Service is an singleton object instance. Use services if you want to provide a singleton object for your code.
- Factory is a class. Use factories if you want to provide custom classes for your code (can't be done with services because they are already instantiated).
The factory 'class' example is provided in the comments around, as well as provider difference.
My clarification on this matter:
Basically all of the mentioned types (service, factory, provider, etc.) are just creating and configuring global variables (that are of course global to the entire application), just as old fashioned global variables were.
While global variables are not recommended, the real usage of these global variables is to provide dependency injection , by passing the variable to the relevant controller.
There are many levels of complications in creating the values for the "global variables":
- 不变
This defines an actual constant that should not be modified during the entire application, just like constants in other languages are (something that JavaScript lacks). - 值
This is a modifiable value or object, and it serves as some global variable, that can even be injected when creating other services or factories (see further on these). However, it must be a " literal value ", which means that one has to write out the actual value, and cannot use any computation or programming logic (in other words 39 or myText or {prop: "value"} are OK, but 2 +2 is not). - 厂
A more general value, that is possible to be computed right away. It works by passing a function to AngularJS with the logic needed to compute the value and AngularJS executes it, and it saves the return value in the named variable.
Note that it is possible to return a object (in which case it will function similar to a service ) or a function (that will be saved in the variable as a callback function). - 服务
A service is a more stripped-down version of factory which is valid only when the value is an object, and it allows for writing any logic directly in the function (as if it would be a constructor), as well as declaring and accessing the object properties using the this keyword. - Provider
Unlike a service which is a simplified version of factory , a provider is a more complex, but more flexible way of initializing the "global" variables, with the biggest flexibility being the option to set values from the app.config.
It works like using a combination of service and provider , by passing to provider a function that has properties declared using the this keyword, which can be used from theapp.config
.
Then it needs to have a separate $.get function which is executed by AngularJS after setting the above properties via theapp.config
file , and this $.get function behaves just as the factory above, in that its return value is used to initialize the "global" variables.
My understanding is very simple below.
Factory: You simply create an object inside of the factory and return it.
服务:
You just have a standard function that uses this keyword to define a function.
提供者:
There is a $get
object that you define and it can be used to get the object that returns data.
Summary from Angular docs :
- There are five recipe types that define how to create objects: Value , Factory , Service , Provider and Constant .
- Factory and Service are the most commonly used recipes. The only difference between them is that the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript primitives and functions.
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
- Provider is the most complex recipe type. You don't need it unless you are building a reusable piece of code that needs global configuration.
Best answers from SO:
https://stackoverflow.com/a/26924234/165673 (<– GOOD) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673
An additional clarification is that factories can create functions/primitives, while services cannot. Check out this jsFiddle based on Epokk's: http://jsfiddle.net/skeller88/PxdSP/1351/ .
The factory returns a function that can be invoked:
myApp.factory('helloWorldFromFactory', function() { return function() { return "Hello, World!"; }; });
The factory can also return an object with a method that can be invoked:
myApp.factory('helloWorldFromFactory', function() { return { sayHello: function() { return "Hello, World!"; } }; });
The service returns an object with a method that can be invoked:
myApp.service('helloWorldFromService', function() { this.sayHello = function() { return "Hello, World!"; }; });
For more details, see a post I wrote on the difference: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/
There are good answers already, but I just want to share this one.
First of all: Provider is the way/recipe to create a service
(singleton object) that suppose to be injected by $injector (how AngulaJS goes about IoC pattern).
And Value, Factory, Service and Constant (4 ways) – the syntactic sugar over Provider way/recepie.
There is Service vs Factory
part has been covered: https://www.youtube.com/watch?v=BLzNCkPn3ao
Service is all about new
keyword actually which as we know does 4 things:
- creates brand new object
- links it to its
prototype
object - connects
context
tothis
- and returns
this
And Factory is all about Factory Pattern – contains functions that return Objects like that Service.
- ability to use other services (have dependencies)
- service initialization
- delayed/lazy initialization
And this simple/short video: covers also Provider : https://www.youtube.com/watch?v=HvTZbQ_hUZY (there you see can see how they go from factory to provider)
Provider recipe is used mostly in the app config, before the app has fully started/initialized.
Here is some broilerplate code I've come up with as a code-template for object factory in AngularjS. I've used a Car/CarFactory as an example to illustrate. Makes for simple implementation code in the controller.
<script> angular.module('app', []) .factory('CarFactory', function() { /** * BroilerPlate Object Instance Factory Definition / Example */ this.Car = function() { // initialize instance properties angular.extend(this, { color : null, numberOfDoors : null, hasFancyRadio : null, hasLeatherSeats : null }); // generic setter (with optional default value) this.set = function(key, value, defaultValue, allowUndefined) { // by default, if (typeof allowUndefined === 'undefined') { // we don't allow setter to accept "undefined" as a value allowUndefined = false; } // if we do not allow undefined values, and.. if (!allowUndefined) { // if an undefined value was passed in if (value === undefined) { // and a default value was specified if (defaultValue !== undefined) { // use the specified default value value = defaultValue; } else { // otherwise use the class.prototype.defaults value value = this.defaults[key]; } // end if/else } // end if } // end if // update this[key] = value; // return reference to this object (fluent) return this; }; // end this.set() }; // end this.Car class definition // instance properties default values this.Car.prototype.defaults = { color: 'yellow', numberOfDoors: 2, hasLeatherSeats: null, hasFancyRadio: false }; // instance factory method / constructor this.Car.prototype.instance = function(params) { return new this.constructor() .set('color', params.color) .set('numberOfDoors', params.numberOfDoors) .set('hasFancyRadio', params.hasFancyRadio) .set('hasLeatherSeats', params.hasLeatherSeats) ; }; return new this.Car(); }) // end Factory Definition .controller('testCtrl', function($scope, CarFactory) { window.testCtrl = $scope; // first car, is red, uses class default for: // numberOfDoors, and hasLeatherSeats $scope.car1 = CarFactory .instance({ color: 'red' }) ; // second car, is blue, has 3 doors, // uses class default for hasLeatherSeats $scope.car2 = CarFactory .instance({ color: 'blue', numberOfDoors: 3 }) ; // third car, has 4 doors, uses class default for // color and hasLeatherSeats $scope.car3 = CarFactory .instance({ numberOfDoors: 4 }) ; // sets an undefined variable for 'hasFancyRadio', // explicitly defines "true" as default when value is undefined $scope.hasFancyRadio = undefined; $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true); // fourth car, purple, 4 doors, // uses class default for hasLeatherSeats $scope.car4 = CarFactory .instance({ color: 'purple', numberOfDoors: 4 }); // and then explicitly sets hasLeatherSeats to undefined $scope.hasLeatherSeats = undefined; $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true); // in console, type window.testCtrl to see the resulting objects }); </script>
Here is a simpler example. I'm using a few third party libraries that expect a "Position" object exposing latitude and longitude, but via different object properties. I didn't want to hack the vendor code, so I adjusted the "Position" objects I was passing around.
angular.module('app') .factory('PositionFactory', function() { /** * BroilerPlate Object Instance Factory Definition / Example */ this.Position = function() { // initialize instance properties // (multiple properties to satisfy multiple external interface contracts) angular.extend(this, { lat : null, lon : null, latitude : null, longitude : null, coords: { latitude: null, longitude: null } }); this.setLatitude = function(latitude) { this.latitude = latitude; this.lat = latitude; this.coords.latitude = latitude; return this; }; this.setLongitude = function(longitude) { this.longitude = longitude; this.lon = longitude; this.coords.longitude = longitude; return this; }; }; // end class definition // instance factory method / constructor this.Position.prototype.instance = function(params) { return new this.constructor() .setLatitude(params.latitude) .setLongitude(params.longitude) ; }; return new this.Position(); }) // end Factory Definition .controller('testCtrl', function($scope, PositionFactory) { $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123}); $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333}); }) // end controller
;
Using as reference this page and the documentation (which seems to have greatly improved since the last time I looked), I put together the following real(-ish) world demo which uses 4 of the 5 flavours of provider; Value, Constant, Factory and full blown Provider.
HTML:
<div ng-controller="mainCtrl as main"> <h1>{{main.title}}*</h1> <h2>{{main.strapline}}</h2> <p>Earn {{main.earn}} per click</p> <p>You've earned {{main.earned}} by clicking!</p> <button ng-click="main.handleClick()">Click me to earn</button> <small>* Not actual money</small> </div>
app
var app = angular.module('angularProviders', []); // A CONSTANT is not going to change app.constant('range', 100); // A VALUE could change, but probably / typically doesn't app.value('title', 'Earn money by clicking'); app.value('strapline', 'Adventures in ng Providers'); // A simple FACTORY allows us to compute a value @ runtime. // Furthermore, it can have other dependencies injected into it such // as our range constant. app.factory('random', function randomFactory(range) { // Get a random number within the range defined in our CONSTANT return Math.random() * range; }); // A PROVIDER, must return a custom type which implements the functionality // provided by our service (see what I did there?). // Here we define the constructor for the custom type the PROVIDER below will // instantiate and return. var Money = function(locale) { // Depending on locale string set during config phase, we'll // use different symbols and positioning for any values we // need to display as currency this.settings = { uk: { front: true, currency: '£', thousand: ',', decimal: '.' }, eu: { front: false, currency: '€', thousand: '.', decimal: ',' } }; this.locale = locale; }; // Return a monetary value with currency symbol and placement, and decimal // and thousand delimiters according to the locale set in the config phase. Money.prototype.convertValue = function(value) { var settings = this.settings[this.locale], decimalIndex, converted; converted = this.addThousandSeparator(value.toFixed(2), settings.thousand); decimalIndex = converted.length - 3; converted = converted.substr(0, decimalIndex) + settings.decimal + converted.substr(decimalIndex + 1); converted = settings.front ? settings.currency + converted : converted + settings.currency; return converted; }; // Add supplied thousand separator to supplied value Money.prototype.addThousandSeparator = function(value, symbol) { return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol); }; // PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY // are all effectively syntactic sugar built on top of the PROVIDER construct // One of the advantages of the PROVIDER is that we can configure it before the // application starts (see config below). app.provider('money', function MoneyProvider() { var locale; // Function called by the config to set up the provider this.setLocale = function(value) { locale = value; }; // All providers need to implement a $get method which returns // an instance of the custom class which constitutes the service this.$get = function moneyFactory() { return new Money(locale); }; }); // We can configure a PROVIDER on application initialisation. app.config(['moneyProvider', function(moneyProvider) { moneyProvider.setLocale('uk'); //moneyProvider.setLocale('eu'); }]); // The ubiquitous controller app.controller('mainCtrl', function($scope, title, strapline, random, money) { // Plain old VALUE(s) this.title = title; this.strapline = strapline; this.count = 0; // Compute values using our money provider this.earn = money.convertValue(random); // random is computed @ runtime this.earned = money.convertValue(0); this.handleClick = function() { this.count ++; this.earned = money.convertValue(random * this.count); }; });
Working demo .
I know a lot of excellent answer but I have to share my experience of using
1. service
for most cases of default
2. factory
used to create the service that specific instance
// factory.js //////////////////////////// (function() { 'use strict'; angular .module('myApp.services') .factory('xFactory', xFactoryImp); xFactoryImp.$inject = ['$http']; function xFactoryImp($http) { var fac = function (params) { this._params = params; // used for query params }; fac.prototype.nextPage = function () { var url = "/_prc"; $http.get(url, {params: this._params}).success(function(data){ ... } return fac; } })(); // service.js ////////////////////////// (function() { 'use strict'; angular .module('myApp.services') .service('xService', xServiceImp); xServiceImp.$inject = ['$http']; function xServiceImp($http) { this._params = {'model': 'account','mode': 'list'}; this.nextPage = function () { var url = "/_prc"; $http.get(url, {params: this._params}).success(function(data){ ... } } })();
and using:
controller: ['xFactory', 'xService', function(xFactory, xService){ // books = new instance of xFactory for query 'book' model var books = new xFactory({'model': 'book', 'mode': 'list'}); // accounts = new instance of xFactory for query 'accounts' model var accounts = new xFactory({'model': 'account', 'mode': 'list'}); // accounts2 = accounts variable var accounts2 = xService; ...
This answer address the topic/question
how Factory, Service and Constant — are just syntactic sugar on top of a provider recipe?
要么
how factory ,servic and providers are simailar internally
basically what happens is
When you make a factory()
it sets you function
provided in second argument to provider's $get
and return it( provider(name, {$get:factoryFn })
), all you get is provider
but there is no property/method other than $get
of that provider
(means you can't configure this)
Source code of factory
function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn }); };
When making a service()
it return you providing a factory() with a function
that injects the constructor
(return the instance of the constructor you provided in your service) and returns it
Source code of service
function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); };
So basically in both cases you eventually get a providers $get set to your function you provided , but you can give anything extra than $get as you can originally provide in provider() for config block
Little late to the party. But I thought this is more helpful for who would like to learn (or have clarity) on developing Angular JS Custom Services using factory, service and provider methodologies.
I came across this video which explains clearly about factory, service and provider methodologies for developing AngularJS Custom Services:
https://www.youtube.com/watch?v=oUXku28ex-M
Source Code: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service
Code posted here is copied straight from the above source, to benefit readers.
The code for "factory" based custom service is as follows (which goes with both sync and async versions along with calling http service):
var app = angular.module("app", []); app.controller('emp', ['$scope', 'calcFactory', function($scope, calcFactory) { $scope.a = 10; $scope.b = 20; $scope.doSum = function() { //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous $scope.sum = r; }); }; } ]); app.factory('calcFactory', ['$http', '$log', function($http, $log) { $log.log("instantiating calcFactory.."); var oCalcService = {}; //oCalcService.getSum = function(a,b){ // return parseInt(a) + parseInt(b); //}; //oCalcService.getSum = function(a, b, cb){ // var s = parseInt(a) + parseInt(b); // cb(s); //}; oCalcService.getSum = function(a, b, cb) { //using http service $http({ url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b, method: 'GET' }).then(function(resp) { $log.log(resp.data); cb(resp.data); }, function(resp) { $log.error("ERROR occurred"); }); }; return oCalcService; } ]);
Just to clarify things, from the AngularJS source, you can see a service just calls the factory function which in turn calls the provider function:
function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); }
Let's discuss the three ways of handling business logic in AngularJS in a simple way: ( Inspired by Yaakov's Coursera AngularJS course )
SERVICE :
句法:
app.js
var app = angular.module('ServiceExample',[]); var serviceExampleController = app.controller('ServiceExampleController', ServiceExampleController); var serviceExample = app.service('NameOfTheService', NameOfTheService); ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files function ServiceExampleController(NameOfTheService){ serviceExampleController = this; serviceExampleController.data = NameOfTheService.getSomeData(); } function NameOfTheService(){ nameOfTheService = this; nameOfTheService.data = "Some Data"; nameOfTheService.getSomeData = function(){ return nameOfTheService.data; } }
的index.html
<div ng-controller = "ServiceExampleController as serviceExample"> {{serviceExample.data}} </div>
Features of Service:
- Lazily Instantiated : If it is not injected it won't be instantiated ever. So to use it will have to inject it to a module.
- Singleton : If injected to multiple modules, all will have access to only one particular instance. That is why very convenient to share data across different controllers.
FACTORY
First let's have a look at the syntax:
app.js :
var app = angular.module('FactoryExample',[]); var factoryController = app.controller('FactoryController', FactoryController); var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne); var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo); //first implementation where it returns a function function NameOfTheFactoryOne(){ var factory = function(){ return new SomeService(); } return factory; } //second implementation where an object literal would be returned function NameOfTheFactoryTwo(){ var factory = { getSomeService : function(){ return new SomeService(); } }; return factory; }
Now using the above two in the controller:
var factoryOne = NameOfTheFactoryOne() //since it returns a function factoryOne.someMethod(); var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object factoryTwo.someMethod();
Features of Factory:
- Follows the factory design pattern. The factory is a central place that produces new objects or functions.
- Not only produces singleton, but customizable services.
- The
.service()
method is a factory that always produces the same type of service, which is a singleton, and without any easy way to configure it's behavior. That.service()
method is usually used as a shortcut for something that doesn't require any configuration whatsoever.
提供商
Let's again have a look at the Syntax first:
angular.module('ProviderModule', []) .controller('ProviderModuleController', ProviderModuleController) .provider('ServiceProvider', ServiceProvider) .config(Config); //optional Config.$inject = ['ServiceProvider']; function Config(ServiceProvider) { ServiceProvider.defaults.maxItems = 10; //some default value } ProviderModuleController.$inject = ['ServiceProvider']; function ProviderModuleController(ServiceProvider) { //some methods } function ServiceProvider() { var provider = this; provider.defaults = { maxItems: 10 }; provider.$get = function () { var someList = new someListService(provider.defaults.maxItems); return someList; }; } }
Features of Provider:
- Provider is the most flexible method of creating services in Angular.
- Not only we can create a factory that's dynamically configurable, but at the time of using the factory, with the provider method, we could custom configure the factory just once at the bootstrapping of our entire application.
- The factory can then be used throughout the application with custom settings. In other words, we can configure this factory before the application starts. In fact in the angular documentation it is mentioned that the provider method is what actually gets executed behind the scenes when we configure our services with either
.service
or.factory
methods. - The
$get
is a function that is directly attached to the provider instance. That function is a factory function. In other words, it's just like the one that we use to provide to the.factory
method. In that function, we create our own service. This$get
property, that's a function, is what makes the provider a provider . AngularJS expects the provider to have a $get property whose value is a function that Angular will treat as a factory function. But what makes this whole provider setup very special, is the fact that we can provide someconfig
object inside the service provider, and that usually comes with defaults that we can later overwrite in the step, where we can configure the entire application.
Factory: The factory you actually create an object inside of the factory and return it.
service: The service you just have a standard function that uses the this keyword to define function.
provider: The provider there's a $get you define and it can be used to get the object that returns the data.
After reading all these post It created more confuse for me.. But still all is worthfull information.. finally I found following table which will give information with simple comparision
- The injector uses recipes to create two types of objects: services and special purpose objects
- There are five recipe types that define how to create objects: Value, Factory, Service, Provider and Constant.
- Factory and Service are the most commonly used recipes. The only difference between them is that the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript primitives and functions.
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
- Provider is the most complex recipe type. You don't need it unless you are building a reusable piece of code that needs global configuration.
- All special purpose objects except for the Controller are defined via Factory recipes.
And for beginner understand:- This may not correct use case but in high level this is what usecase for these three.
- If you want to use in angular module config function should created as provider
angular.module('myApp').config(function($testProvider){ $testProvider.someFunction(); })
Essentially, Provider, Factory, and Service are all Services. A Factory is a special case of a Service when all you need is a $get() function, allowing you to write it with less code.
The major differences among Services, Factories, and Providers are their complexities. Services are the simplest form, Factories are a little more robust, and Providers are configurable at runtime.
Here is a summary of when to use each:
Factory : The value you are providing needs to be calculated based on other data.
Service : You are returning an object with methods.
Provider : You want to be able to configure, during the config phase, the object that is going to be created before it's created. Use the Provider mostly in the app config, before the app has fully initialized.
1.Services are singleton objects that are created when necessary and are never cleaned up until the end of the application life-cycle (when the browser is closed). Controllers are destroyed and cleaned up when they are no longer needed.
2.The easiest way to create a service is by using the factory() method. The factory() method allows us to define a service by returning an object that contains service functions and service data. The service definition function is where we place our injectable services, such as $http and $q. 例如:
angular.module('myApp.services') .factory('User', function($http) { // injectables go here var backendUrl = "http://localhost:3000"; var service = { // our factory definition user: {}, setName: function(newName) { service.user['name'] = newName; }, setEmail: function(newEmail) { service.user['email'] = newEmail; }, save: function() { return $http.post(backendUrl + '/users', { user: service.user }); } }; return service; });
Using the factory() in our app
It's easy to use the factory in our application as we can simply inject it where we need it at run-time.
angular.module('myApp') .controller('MainController', function($scope, User) { $scope.saveUser = User.save; });
- The service() method, on the other hand allows us to create a service by defining a constructor function. We can use a prototypical object to define our service, instead of a raw javascript object. Similar to the factory() method, we'll also set the injectables in the function definition.
- The lowest level way to create a service is by using the provide() method. This is the only way to create a service that we can configure using the .config() function. Unlike the previous to methods, we'll set the injectables in a defined this.$get() function definition.
All the good answers already. I would like to add few more points on Service and Factory . Along with the difference between service/factory. And one can also have questions like:
- Should I use service or factory? What's the difference?
- Is they do same or have same behaviour?
Lets start with the difference between Service and factory:
-
Both are Singletons : Whenever Angular find these as a dependency first time,it create a single instance of service/factory. Once the instance is created, same instance is used forever.
-
Can be used to model an object with behavior : They can both have methods, internal state variables, and so on. Though the way you write that code will differ.
服务:
A service is a constructor function, and Angular will instantiate it by calling new yourServiceName()
. This means a couple of things.
- Functions and instance variables will be properties of
this
. - You don't need to return a value. When Angular calls
new yourServiceName(
), it'll receive thethis
object with all the properties you put on it.
Sample Example:
angular.service('MyService', function() { this.aServiceVariable = "Ved Prakash" this.aServiceMethod = function() { return //code }; });
When Angular injects this
MyService
service into a controller that depends on it, that controller will get aMyService
that it can call functions on, eg MyService.aServiceMethod ().
Be careful with this
:
Since the constructed service is an object, the methods inside it can refer to this when they're called:
angular.service('ScoreKeeper', function($http) { this.score = 0; this.getScore = function() { return this.score; }; this.setScore = function(newScore) { this.score = newScore; }; this.addOne = function() { this.score++; }; });
You might be tempted to call ScoreKeeper.setScore
in a promise chain, for instance if you initialized the score by grabbing it from the server: $http.get('/score').then(ScoreKeeper.setScore).
The trouble with this is that ScoreKeeper.setScore
will be called with this
bound to null
and you'll get errors. The better way would be $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))
. Whether you choose to use this in your service methods or not, be careful how you call them.
Returning a Value from a Service
:
Due to how JavaScript constructors work, if you return a complex value (ie, an Object)
from a constructor
function, the caller will get that Object instead of the this instance.
This means that you can basically copy-paste the factory example from below, replace factory
with service
, and it'll work:
angular.service('MyService', function($http) { var api = {}; api.aServiceMethod= function() { return $http.get('/users'); }; return api; });
So when Angular constructs your service with new MyService(), it'll get that api object instead of the MyService instance.
This is the behavior for any complex values (objects, functions) but not for primitive types.
Factories:
A factory is a plain old function that returns a value. The return value is what gets injected into things that depend on the factory. A typical factory pattern in Angular is to return an object with functions as properties, like this:
angular.factory('MyFactory', function($http) { var api = {}; api.aFactoryMethod= function() { return $http.get('/users'); }; return api; });
The injected value for a factory dependency is the factory's return value, and it doesn't have to be an object. It could be a function
Answers for above 1 and 2 questions:
For the most part, just stick with using factories for everything. Their behavior is easier to understand. There's no choice to make about whether to return a value or not, and furthermore, no bugs to be introduced if you do the wrong thing.
I still refer to them as “services” when I'm talking about injecting them as dependencies, though.
Service/Factory behavior is very similar, and some people will say that either one is fine. That's somewhat true, but I find it easier to follow the advice of John Papa's style guide and just stick with factories.**
Syntactic Sugar is the difference . Only provider is needed. Or in other words only provider is the real angular, all other ones are derived(to reduce code). There is a simple version as well, called Value() which returns just the value, no calculation or function. Even Value is derived from provider!
So why such complications, why can't we just use provider and forget everything else? It is supposed to help us write code easily and communicate better. And toungue-in-cheek reply would be, the more complex it gets the better selling a framework will be.
- A provider that can return value = Value
- A provider that can just instantiate and return = Factory (+ Value)
- A provider that can instantiate + do something = Service (+ Factory, + Value)
- A provider = must contain a property called $get (+Factory, + Service, + Value)
Angular injection gives us the first hint in reaching this conclusion.
"$injector is used to retrieve object instances as defined by provider " not service, not factory but provider.
And a better answer would be this: "An Angular service is created by a service factory. These service factories are functions which, in turn, are created by a service provider. The service providers are constructor functions. When instantiated they must contain a property called $get, which holds the service factory function."
So master provider and injector and all will fall in place 🙂 . And it gets interesting in Typescript when $get can be implemented in a provider by inheriting from IServiceProvider.