AngularJS: Service vs provider vs factory
AngularJS中的
从AngularJS邮件列表中,我得到了一个令人惊叹的线程,解释了服务与工厂与供应商之间的关系以及它们的注入使用情况。整理答案:
服务语法:
结果:将servicename声明为可注入参数时,将向您提供函数的实例。换言之,
语法:
结果:将FactoryName声明为可注入参数时,将向您提供通过调用传递给module.factory的函数引用返回的值。
语法:
结果:当将providername声明为可注入参数时,将向您提供
提供程序的优点是,它们可以在模块配置阶段进行配置。
有关提供的代码,请参阅此处。
Misko的进一步解释是:
1 2 3 4 5 | provide.value('a', 123); function Controller(a) { expect(a).toEqual(123); } |
在这种情况下,注入器只返回原样的值。但是如果你想计算这个值呢?然后使用工厂
1 2 3 4 5 6 7 | provide.factory('b', function(a) { return a*2; }); function Controller(b) { expect(b).toEqual(246); } |
因此,
但是,如果你想做更多的OO并且有一个叫做"迎宾员"的班级呢?
1 2 3 4 5 | function Greeter(a) { this.greet = function() { return 'Hello ' + a; } } |
然后要实例化,您必须编写
1 2 3 | provide.factory('greeter', function(a) { return new Greeter(a); }); |
然后我们可以在控制器里这样叫"迎宾员"
1 2 3 4 | function Controller(greeter) { expect(greeter instanceof Greeter).toBe(true); expect(greeter.greet()).toEqual('Hello 123'); } |
但这太冗长了。写这个的一个较短的方法是
但是如果我们想在注入之前配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); }; }); |
然后我们可以这样做:
1 2 3 4 5 6 7 | angular.module('abc', []).config(function(greeter2Provider) { greeter2Provider.setSalutation('Halo'); }); function Controller(greeter2) { expect(greeter2.greet()).toEqual('Halo 123'); } |
作为补充说明,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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小提琴演示"Hello World"示例中的
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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()]; } |
1 2 3 4 5 6 | <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"> <body ng-app="myApp"> {{hellos}} </body> |
DR1)使用工厂时,创建对象,向其添加属性,然后返回同一对象。当您将此工厂传递到控制器中时,对象上的这些属性现在将通过工厂在该控制器中可用。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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)使用服务时,AngularJS在后台用"new"关键字实例化它。因此,您将向"this"添加属性,服务将返回"this"。当您将服务传递到控制器时,"this"上的那些属性现在将通过您的服务在该控制器上可用。好的。
1 2 3 4 5 6 7 8 9 10 | app.controller(‘myServiceCtrl’, function($scope, myService){ $scope.artist = myService.getArtist(); }); app.service(‘myService’, function(){ var _artist = ‘Nelly’; this.getArtist = function(){ return _artist; } }); |
3)提供程序是唯一可以传递到.config()函数中的服务。如果要在使服务对象可用之前为其提供模块范围的配置,请使用提供程序。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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好的。
1)工厂工厂是创建和配置服务最常用的方法。"其实没有什么比TL更重要了,"博士说。您只需创建一个对象,向其添加属性,然后返回同一个对象。然后,当您将工厂传递到控制器中时,对象上的这些属性现在将通过工厂在该控制器中可用。下面是一个更广泛的例子。好的。
1 2 3 4 | app.factory(‘myFactory’, function(){ var service = {}; return service; }); |
现在,当我们将"MyFactory"传递到控制器时,我们可以使用附加到"Service"的任何属性。好的。
现在,让我们向回调函数添加一些"private"变量。这些不能从控制器直接访问,但我们最终将在"service"上设置一些getter/setter方法,以便在需要时更改这些"private"变量。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; }); |
在这里,您会注意到我们没有将这些变量/函数附加到"service"。我们只是创建它们,以便稍后使用或修改它们。好的。
- base url是iTunes API需要的基本URL
- _艺术家是我们要查找的艺术家
- _ finallor是最后一个完整构建的URL,我们将调用iTunes。
- makeurl是一个创建和返回iTunes友好URL的函数。
既然我们的helper/private变量和函数已经就位,那么让我们向"service"对象添加一些属性。无论我们在"服务"上做什么,都可以直接在我们通过"我的工厂"进入的任何控制器内使用。好的。
我们将创建set artist和getartist方法,这些方法只返回或设置艺术家。我们还将创建一个方法,用我们创建的URL调用iTunes API。这个方法将返回一个承诺,一旦数据从iTunesAPI返回,这个承诺就会实现。如果你在安古拉基没有太多的使用承诺的经验,我强烈建议你深入研究一下。好的。
下面的set artist接受一个艺术家并允许您设置该艺术家。GetArtist返回艺术家。callitunes首先调用makeurl(),以便构建将用于$http请求的URL。然后它设置一个promise对象,用我们的最终URL发出一个$http请求,然后因为$http返回一个promise,我们可以在请求之后调用.success或.error。然后,我们用iTunes数据来解决我们的承诺,或者我们用一条消息拒绝它,说"有一个错误"。好的。
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 29 30 31 32 33 34 35 36 | 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)。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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"的数据设置$scope对象的属性。上面唯一棘手的代码是如果你以前从未处理过承诺。因为callitunes返回了一个承诺,所以我们可以使用.then()方法,并且只有在我们的承诺完成之后,才能设置$scope.data.artistdata。您会注意到我们的控制器非常"薄"(这是一个很好的编码实践)。我们所有的逻辑和持久数据都位于我们的服务中,而不是在我们的控制器中。2)服务在处理创建服务时,要知道的最大的事情可能是它是用"new"关键字实例化的。对于您的javascript专家来说,这应该给您一个关于代码性质的大提示。对于那些在javascript中背景有限或不太熟悉"new"关键字实际作用的人,让我们回顾一些javascript基本原理,这些基本原理最终将帮助我们了解服务的性质。好的。
要真正了解使用"new"关键字调用函数时所发生的更改,让我们创建一个函数并使用"new"关键字调用它,然后让我们展示解释器在看到"new"关键字时所做的操作。最终结果将是相同的。好的。
首先,让我们创建构造函数。好的。
1 2 3 4 | var Person = function(name, age){ this.name = name; this.age = age; } |
这是一个典型的javascript构造函数函数。现在,每当我们使用"new"关键字调用person函数时,"this"都将绑定到新创建的对象。好的。
现在,让我们将一个方法添加到我们人员的原型中,这样它就可以在我们人员"类"的每个实例上使用。好的。
1 2 3 | Person.prototype.sayName = function(){ alert(‘My name is ‘ + this.name); } |
现在,因为我们在原型上放置了sayname函数,所以每个人的实例都可以调用sayname函数来警告该实例的名称。好的。
既然我们在原型上有了我们的Person构造函数函数和sayname函数,那么让我们实际创建一个Person实例,然后调用sayname函数。好的。
1 2 | var tyler = new Person(‘Tyler’, 23); tyler.sayName(); //alerts ‘My name is Tyler’ |
因此,创建一个Person构造函数、向其原型中添加一个函数、创建一个Person实例以及在其原型上调用该函数的所有代码都是这样的。好的。
1 2 3 4 5 6 7 8 9 | 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()时,解释器会说"好的,我将查看刚才创建的"tyler"对象,找到sayname函数,然后调用它。等一下,我在这里看不到-我只看到名字和年龄,让我检查一下原型。是的,看起来像是在原型上,让我称之为。好的。
下面是关于如何思考"new"关键字在javascript中实际执行的操作的代码。它基本上是上面段落的代码示例。我已经将"解释器视图"或解释器在Notes中看到代码的方式放入。好的。
1 2 3 4 5 6 7 8 9 10 11 12 | 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创建服务应该更容易理解。好的。
创建服务时要理解的最大的事情是知道服务是用"new"关键字实例化的。将这些知识与上面的示例结合起来,您现在应该认识到您将直接将属性和方法附加到"this",然后将从服务本身返回。让我们来看看这个。好的。
与我们最初对工厂示例所做的不同,我们不需要创建一个对象,然后返回该对象,因为像前面提到的很多次一样,我们使用了"new"关键字,这样解释器将创建该对象,让它委托给它的原型,然后为我们返回它,而不需要我们做这些工作。好的。
首先,让我们创建"private"和helper函数。这看起来应该很熟悉,因为我们对工厂做了同样的事情。我不会解释每一行在这里做什么,因为我在工厂示例中这样做了,如果您感到困惑,请重新阅读工厂示例。好的。
1 2 3 4 5 6 7 8 9 10 11 | 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"上。好的。
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 29 30 31 32 33 34 | 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传递到的任何控制器中都可用。这里是myservice控制器(几乎与我们的工厂控制器完全相同)。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); }) } }); |
正如我之前提到的,一旦你真正了解"新"的功能,服务就几乎与安古拉吉的工厂完全相同。3)提供者好的。
关于提供者,要记住的最大一点是,它们是唯一可以传递到应用程序app.config部分的服务。如果您需要在服务对象的某些部分在应用程序中的其他地方可用之前进行更改,那么这一点非常重要。尽管与服务/工厂非常相似,但我们将讨论一些差异。好的。
首先,我们以与我们的服务和工厂类似的方式建立供应商。下面的变量是我们的"private"和helper函数。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } } |
*同样,如果上面代码的任何部分令人困惑,请查看工厂部分,在这里我将解释它的所有功能,并提供更详细的信息。好的。
您可以认为提供者有三个部分。第一部分是"私有"变量/函数,稍后将进行修改/设置(如上所示)。第二部分是可以在app.config函数中使用的变量/函数,因此可以在它们在其他任何地方可用之前进行更改(如上所示)。需要注意的是,这些变量需要附加到"this"关键字上。在我们的示例中,只有"thingfromconfig"可以在app.config中更改。第三部分(如下所示)是当您将"myprovider"服务传递到特定控制器时,控制器中可用的所有变量/函数。好的。
使用提供程序创建服务时,控制器中唯一可用的属性/方法是从$get()函数返回的属性/方法。下面的代码将$GET放在"this"上(我们知道它最终将从该函数返回)。现在,$get函数返回我们希望在控制器中可用的所有方法/属性。下面是一个代码示例。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 } } |
现在完整的提供程序代码如下所示好的。
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 29 30 31 32 33 34 35 36 37 38 39 | 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控制器(几乎与我们的工厂/服务控制器完全相同)。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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创建服务的整个要点是,在将最终对象传递给应用程序的其余部分之前,能够通过app.config函数更改一些变量。让我们来看一个例子。好的。
1 2 3 4 | 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"在我们的提供者中是如何作为空字符串出现的,但是当它出现在DOM中时,它将是"这个句子被设置了……"。好的。好啊。
所有服务都是单例的;每个应用程序实例化一次。它们可以是任何类型,无论是基元、对象文本、函数,甚至是自定义类型的实例。
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 on top of a provider recipe.
- 值配方是最简单的情况,您可以自己实例化服务并向注入器提供实例化的值。
- 工厂配方为注入器提供了一个工厂函数,在需要实例化服务时调用该函数。调用时,factory函数创建并返回服务实例。服务的依赖项作为函数的参数注入。所以使用这个配方可以增加以下能力:
- 使用其他服务的能力(具有依赖性)
- 服务初始化
- 延迟/延迟初始化
- 服务配方几乎与工厂配方相同,但在这里,注入器使用新的运算符而不是工厂函数调用构造函数。
- 提供者的配方通常是杀伤力过大的。它允许您配置工厂的创建,从而添加了一个间接层。
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. -
The Constant recipe is just like the Value recipe except it allows you to define services that are available in the config phase. Sooner than services created using the Value recipe. Unlike Values, they cannot be decorated using
decorator .
See the provider documentation.
了解AngularJS工厂、服务和供应商
所有这些都用于共享可重用的单例对象。它有助于在应用程序/各种组件/模块之间共享可重用代码。
From Docs Service/Factory:
- Lazily instantiated – Angular only instantiates a service/factory when an application component depends on it.
- Singletons – Each component
dependent on a service gets a reference to the single instance
generated by the service factory.
工厂
工厂是一个函数,您可以在创建对象之前操作/添加逻辑,然后返回新创建的对象。
1 2 3 4 5 6 7 8 9 | app.factory('MyFactory', function() { var serviceObj = {}; //creating an object with methods/functions or variables serviceObj.myFunction = function() { //TO DO: }; //return that object return serviceObj; }); |
用法
它可以是类等函数的集合。因此,当您在控制器/工厂/指令函数中注入它时,它可以在不同的控制器中实例化。每个应用程序只实例化一次。
服务在查看服务时,只需考虑阵列原型。服务是使用"new"关键字实例化新对象的函数。您可以使用
1 2 3 4 5 6 | app.service('MyService', function() { //directly binding events to this context this.myServiceFunction = function() { //TO DO: }; }); |
用法
当需要在整个应用程序中共享单个对象时,可以使用它。例如,经过身份验证的用户详细信息、可共享的方法/数据、实用程序功能等。
供应商提供程序用于创建可配置的服务对象。您可以从配置功能配置服务设置。它使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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. }; }; }); |
用法
当您需要在提供服务对象之前为其提供模块化配置时,例如,假设您希望根据环境(如
NOTE
Only provider will be available in config phase of angular, while
service & factory are not.
希望这能使您对工厂、服务和供应商的了解更加清晰。
对我来说,当我意识到它们都是以相同的方式工作时,启示就出现了:运行一次某个东西,存储它们得到的值,然后在通过依赖注入引用时,将存储的值吐出来。
说我们有:
1 2 3 | app.factory('a', fn); app.service('b', fn); app.provider('c', fn); |
这三者的区别在于:
这意味着在AngularJS中有类似缓存对象的东西,每次注入的值只分配一次,当它们第一次被注入时,并且其中:
1 2 3 | cache.a = fn() cache.b = new fn() cache.c = (new fn()).$get() |
这就是为什么我们在服务中使用
服务vs供应商vs工厂:好的。
我尽量保持简单。这都是关于基本的JavaScript概念。好的。
首先,让我们来谈谈安古拉吉的服务!好的。
什么是服务:在AngularJS中,服务只是一个可以存储一些有用方法或属性的单例JavaScript对象。这个singleton对象是根据ngapp(angular app)创建的,它在当前app中的所有控制器之间共享。当AngularJS实例化一个服务对象时,它用一个唯一的服务名称注册这个服务对象。所以每次我们需要服务实例时,Angular都会在注册表中搜索这个服务名,并返回对服务对象的引用。这样我们就可以在服务对象上调用方法、访问属性等。您可能会有疑问,您是否也可以将属性、方法放在控制器的作用域对象上!为什么需要服务对象?答案是:服务在多个控制器范围内共享。如果您在控制器的作用域对象中放置了一些属性/方法,它将只对当前作用域可用。但是,当您在服务对象上定义方法、属性时,它将全局可用,并且可以通过注入该服务在任何控制器的作用域中进行访问。好的。
因此,如果有三个控制器作用域,即ControllerA、ControllerB和ControllerC,那么它们都将共享同一个服务实例。好的。
1 2 3 4 5 6 7 | <!-- controllerA scope --> <!-- controllerB scope --> <!-- controllerC scope --> |
如何创建服务?好的。
AngularJS提供了注册服务的不同方法。在这里,我们将集中讨论三种方法:工厂(..)、服务(..)、提供商(..);好的。
使用此链接作为代码引用好的。工厂功能:
我们可以定义一个工厂函数如下。好的。
1 | factory('serviceName',function fnFactory(){ return serviceInstance;}) |
AngularJS提供了"factory(‘servicename’,fnfactory)’方法,它采用两个参数servicename和一个javascript函数。Angular通过调用函数fnFactory()创建服务实例,如下所示。好的。
1 | var serviceInstace = fnFactory(); |
传递的函数可以定义一个对象并返回该对象。AngularJS只是将这个对象引用存储到作为第一个参数传递的变量中。从FnFactory返回的任何内容都将绑定到ServiceInstance。除了返回对象之外,我们还可以返回函数、值等,无论我们返回什么,都可以用于服务实例。好的。
例子:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; }); |
服务功能:
1 | service('serviceName',function fnServiceConstructor(){}) |
这是另一种方式,我们可以注册服务。唯一的区别是AngularJS试图实例化服务对象的方式。这次,angular使用"new"关键字,并像下面这样调用构造函数函数。好的。
1 | var serviceInstance = new fnServiceConstructor(); |
在构造函数函数中,我们可以使用"this"关键字向服务对象添加属性/方法。例子:好的。
1 2 3 4 5 6 7 8 9 | //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()函数是创建服务的另一种方法。让我们有兴趣创建一个只向用户显示一些问候语的服务。但我们还希望提供一个功能,这样用户就可以设置自己的问候语。在技术方面,我们希望创建可配置的服务。我们怎么做?必须有一种方法,这样应用程序才能传递他们的自定义问候语,而AngularJS将使它对创建我们的服务实例的工厂/构造函数函数可用。在这种情况下,provider()函数执行该任务。使用provider()函数,我们可以创建可配置的服务。好的。
我们可以使用下面给出的提供者语法创建可配置的服务。好的。
1 2 3 4 5 | /*step1:define a service */ app.provider('service',function serviceProviderConstructor(){}); /*step2:configure the service */ app.config(function configureService(serviceProvider){}); |
提供程序语法如何在内部工作?
1.提供程序对象是使用我们在提供程序函数中定义的构造函数函数创建的。好的。
1 | var serviceProvider = new serviceProviderConstructor(); |
2.我们在app.config()中传递的函数得到执行。这叫做配置阶段,这里我们有机会定制我们的服务。好的。
1 | configureService(serviceProvider); |
3.最后通过调用serviceprovider的$get方法创建服务实例。好的。
1 | serviceInstance = serviceProvider.$get() |
使用provide语法创建服务的示例代码:
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 29 30 31 32 | 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 ?'); } ); |
工作演示好的。
总结:好的。
工厂使用返回服务实例的工厂函数。serviceInstance=fnFactory();好的。
服务使用构造函数函数,并使用"new"关键字调用此构造函数函数来创建服务实例。serviceInstance=new fnServiceConstructor();好的。
provider定义providerconstructor函数,此providerconstructor函数定义工厂函数$get。Angular调用$get()创建服务对象。提供程序语法还有一个额外的优点,即在实例化服务对象之前对其进行配置。serviceInstance=$get();好的。好啊。
正如这里的几个人所指出的,工厂、供应商、服务,甚至价值和常量都是同一事物的版本。你可以把更一般的
这是图片来自的文章:
http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/
工厂
如果给angularjs一个函数,那么在请求工厂时,angularjs将缓存并注入返回值。
例子:
1 2 3 4 5 6 7 | app.factory('factory', function() { var name = ''; // Return value **is** the object that will be injected return { name: name; } }) |
用途:
1 2 3 | app.controller('ctrl', function($scope, factory) { $scope.name = factory.name; }); |
服务
给angularjs一个函数,angularjs会调用new来实例化它。这是AngularJS创建的实例,当请求服务时,它将被缓存和注入。因为new用于实例化服务,所以关键字this是有效的并引用实例。
例子:
1 2 3 4 5 6 7 8 9 | app.service('service', function() { var name = ''; this.setName = function(newName) { name = newName; } this.getName = function() { return name; } }); |
用途:
1 2 3 | app.controller('ctrl', function($scope, service) { $scope.name = service.getName(); }); |
供应商
给angularjs一个函数,angularjs会调用它的
提供程序允许您在AngularJS调用
例子:
1 2 3 4 5 6 7 8 9 10 11 | app.provider('provider', function() { var name = ''; this.setName = function(newName) { name = newName; } this.$get = function() { return { name: name } } }) |
用途(作为控制器中的可注射物)
1 2 3 | app.controller('ctrl', function($scope, provider) { $scope.name = provider.name; }); |
用法(在调用
1 2 3 | app.config(function(providerProvider) { providerProvider.setName('John'); }); |
我在和供应商玩的时候注意到一些有趣的事情。
与服务和工厂相比,供应商对注射物的可视性有所不同。如果声明一个angularJS"常量"(例如,
但是,如果您声明一个AngularJS"值"(例如,
http://jsfidle.net/r2frv/1/
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 29 30 31 | <p> from Service: {{servGreet}} </p> <p> from Provider: {{provGreet}} </p> 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; } |
对于新手来说,这是一个非常令人困惑的部分,我试图用简单易懂的语言来解释它。
AngularJS服务:用于与控制器中的服务引用共享实用程序功能。服务本质上是单例的,因此对于一个服务,在浏览器中只创建一个实例,并且在整个页面中使用相同的引用。
在服务中,我们用这个对象创建函数名作为属性。
AngularJS工厂:工厂的目的也与服务相同,但是在这种情况下,我们创建一个新的对象,并添加函数作为这个对象的属性,最后返回这个对象。
AngularJS提供程序:它的目的也是一样的,但是提供程序给出了$get函数的输出。
定义和使用服务、工厂和供应商在http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider上进行了解释。
对我来说,理解差异的最好和最简单的方法是:
1 2 | var service, factory; service = factory = function(injection) {} |
AngularJS如何实例化特定组件(简化):
1 2 3 4 5 | // service var angularService = new service(injection); // factory var angularFactory = factory(injection); |
因此,对于服务,成为AngularJS组件的是由服务声明函数表示的类的对象实例。对于工厂,它是从工厂声明函数返回的结果。工厂的行为可能与服务相同:
1 2 3 4 5 | var factoryAsService = function(injection) { return new function(injection) { // Service content } } |
最简单的思维方式是:
- 服务是单例对象实例。如果要为代码提供singleton对象,请使用服务。
- 工厂是一流的。如果要为代码提供自定义类,请使用工厂(无法使用服务完成,因为它们已被实例化)。
工厂"类"示例在周围的注释以及提供者差异中提供。
我对此事的澄清:
基本上,所有提到的类型(服务、工厂、提供者等)都只是创建和配置全局变量(当然对整个应用程序是全局的),就像老式的全局变量一样。
虽然不建议使用全局变量,但这些全局变量的实际用途是通过将变量传递给相关控制器来提供依赖注入。
在为"全局变量"创建值时,有许多复杂程度:
这定义了在整个应用程序中不应修改的实际常量,就像其他语言中的常量一样(这是JavaScript所缺少的)。
这是一个可修改的值或对象,它充当一些全局变量,甚至可以在创建其他服务或工厂时注入这些变量(请参阅更多内容)。但是,它必须是"文字值",这意味着必须写出实际值,并且不能使用任何计算或编程逻辑(换句话说,39或mytext或prop:"value"是可以的,但2+2不是)。
请注意,可以返回一个对象(在这种情况下,它的功能类似于服务)或一个函数(它将作为回调函数保存在变量中)。
它的工作方式类似于使用服务和提供程序的组合,通过将一个具有使用this关键字声明的属性的函数传递给提供程序,该关键字可以从
然后它需要有一个单独的$.get函数,该函数在通过
我的理解很简单。
工厂:您只需在工厂内创建一个对象并返回它。
服务:
您只有一个使用这个关键字定义函数的标准函数。
供应商:
您定义了一个
Angular文档摘要:
- 有五种配方类型定义了如何创建对象:值,工厂、服务、供应商和常量。
- 工厂和服务是最常用的配方。它们之间的唯一区别是,服务配方对自定义类型的对象更有效,而工厂可以生成JavaScript原语和函数。
- 提供者的配方是核心配方类型,其他的都只是它上面的语法糖。
- 提供程序是最复杂的配方类型。除非您正在构建一段需要全局配置的可重用代码,否则您不需要它。
SO的最佳答案:
https://stackoverflow.com/a/26924234/165673(<--好)https://stackoverflow.com/a/27263882/165673https://stackoverflow.com/a/16566144/165673
另外一个解释是,工厂可以创建函数/原语,而服务不能。查看这个基于epokk的jspiddle:http://jsfiddle.net/skeler88/pxdsp/1351/。
工厂返回可调用的函数:
1 2 3 4 5 | myApp.factory('helloWorldFromFactory', function() { return function() { return"Hello, World!"; }; }); |
工厂还可以返回具有可调用方法的对象:
1 2 3 4 5 6 7 | myApp.factory('helloWorldFromFactory', function() { return { sayHello: function() { return"Hello, World!"; } }; }); |
服务返回具有可调用方法的对象:
1 2 3 4 5 | myApp.service('helloWorldFromService', function() { this.sayHello = function() { return"Hello, World!"; }; }); |
有关更多详细信息,请参阅我写的一篇关于差异的文章:http://www.shanemkler.com/tldr-services-vs-factures-in-angular/
所有的好答案都已经找到了。我想在服务和工厂方面多加几点。以及服务/工厂之间的差异。你也可以有这样的问题:
让我们从服务和工厂的区别开始:
两者都是单例的:每当Angular第一次发现它们作为依赖项时,它会创建服务/工厂的单个实例。创建实例后,将永远使用相同的实例。
可以使用行为来建模对象:它们都可以有方法、内部状态变量等等。尽管您编写代码的方式会有所不同。
服务:
服务是一个构造函数函数,angular将通过调用new
示例:
1 2 3 4 5 6 | 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, e.g. MyService.aServiceMethod ().
当心
由于构造的服务是一个对象,因此当调用它们时,它内部的方法可以引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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++; }; }); |
你可能会在承诺链中调用
从
由于javascript构造函数的工作方式,如果从
这意味着您基本上可以从下面复制粘贴工厂示例,将
1 2 3 4 5 6 7 8 | angular.service('MyService', function($http) { var api = {}; api.aServiceMethod= function() { return $http.get('/users'); }; return api; }); |
因此,当Angular使用new myService()构造服务时,它将获取该API对象而不是myService实例。
这是任何复杂值(对象、函数)的行为,而不是基元类型的行为。
工厂:
工厂是返回值的普通旧函数。返回值是注入依赖工厂的东西的值。以角度表示的典型工厂模式是返回具有函数作为属性的对象,如下所示:
1 2 3 4 5 6 7 8 9 | 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
以上1和2个问题的答案:
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.**
已经有很好的答案了,但我只想和大家分享一下。
首先:provider是创建一个EDOCX1(singleton对象)的方法/方法,该对象假定由$injector注入(angulajs如何处理ioc模式)。
以及价值、工厂、服务和常量(4种方式)——提供方方式/接受方式的句法糖分。
其中
实际上,服务是关于
工厂是关于工厂模式的——包含返回类似服务的对象的函数。
这个简单/简短的视频:也包括提供商:https://www.youtube.com/watch?v=hvtzbq_huzy(在这里您可以看到它们如何从工厂到供应商)
在应用程序完全启动/初始化之前,提供程序配方主要在应用程序配置中使用。
下面是一些我为AngularJS中的对象工厂设计的代码模板。我以一家汽车/汽车厂为例进行了说明。使控制器中的实现代码变得简单。
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 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 }); |
下面是一个简单的例子。我使用的是一些第三方库,它们期望一个"位置"对象显示纬度和经度,但通过不同的对象属性。我不想破解供应商代码,所以我调整了我传递的"位置"对象。
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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 |
;
在阅读了所有这些文章之后,它给我造成了更多的困惑。但仍然都是有价值的信息。最后,我找到了下表,它将提供简单比较的信息
- 注入器使用配方创建两种类型的对象:服务以及特殊用途的物品
- 有五种配方类型定义了如何创建对象:值,工厂、服务、供应商和常量。
- 工厂和服务是最常用的配方。它们之间的唯一区别是,服务配方对自定义类型的对象更有效,而工厂可以生成JavaScript原语和函数。
- 提供者的配方是核心配方类型,其他的都只是它上面的语法糖。
- 提供程序是最复杂的配方类型。除非您正在构建一段需要全局配置的可重用代码,否则您不需要它。
- 除控制器外的所有特殊用途对象都是通过工厂配方定义的。
对于初学者来说,理解:-这可能不正确的用例,但在高层,这就是这三个用例的用途。
1 2 3 | angular.module('myApp').config(function($testProvider){ $testProvider.someFunction(); }) |
对于基本方案,工厂和服务的行为相同。
此答案涉及主题/问题
工厂、服务和常量——仅仅是供应商食谱上的语法糖吗?或
工厂、服务和供应商如何在内部实现Similar基本上发生的是
当你制作一个
工厂源代码
1 2 3 4 5 | function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn }); }; |
当生成
服务源代码
1 2 3 4 5 | function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); }; |
所以基本上,在这两种情况下,您最终都会得到一个提供者$get设置为您提供的函数,但是您可以提供除$get之外的任何东西,正如您最初在provider()中为config块提供的那样。
作为参考,本页和文档(自上次查看以来似乎有了很大的改进)结合了以下真实的(ish)世界演示,其中使用了提供商的5种风格中的4种:价值、常量、工厂和全面的提供商。
HTML:
1 2 3 4 5 6 7 8 9 10 | {{main.title}}* {{main.strapline}} <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> |
应用程序
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 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); }; }); |
工作演示。
我知道很多很好的答案,但我必须分享我的使用经验1。
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 29 30 31 32 33 34 35 36 37 38 39 40 | // 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){ ... } } })(); |
并使用:
1 2 3 4 5 6 7 8 9 10 11 | 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; ... |
参加聚会有点晚。但是,我认为这对于那些希望学习(或清楚地了解)使用工厂、服务和提供者方法开发AngularJS定制服务的人来说更有用。
我看过这段视频,它清楚地解释了开发AngularJS定制服务的工厂、服务和提供商方法:
https://www.youtube.com/watch?V= OXXKUX-EX-M
源代码:http://www.techcbt.com/post/353/angular-js-basics/how-to-develop-angular js-custom-service
这里发布的代码是直接从上面的源代码复制的,以使读者受益。
基于"工厂"的自定义服务的代码如下(与同步和异步版本以及调用HTTP服务一起使用):
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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; } ]); |
自定义服务的"服务"方法的代码(这与"工厂"非常相似,但与语法观点不同):
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 29 30 31 32 33 34 35 36 37 38 39 40 | var app = angular.module("app", []); app.controller('emp', ['$scope', 'calcService', function($scope, calcService){ $scope.a = 10; $scope.b = 20; $scope.doSum = function(){ //$scope.sum = calcService.getSum($scope.a, $scope.b); calcService.getSum($scope.a, $scope.b, function(r){ $scope.sum = r; }); }; }]); app.service('calcService', ['$http', '$log', function($http, $log){ $log.log("instantiating calcService.."); //this.getSum = function(a,b){ // return parseInt(a) + parseInt(b); //}; //this.getSum = function(a, b, cb){ // var s = parseInt(a) + parseInt(b); // cb(s); //}; this.getSum = function(a, b, cb){ $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"); }); }; }]); |
自定义服务的"提供者"方法的代码(如果您希望开发可配置的服务,这是必需的):
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | var app = angular.module("app", []); app.controller('emp', ['$scope', 'calcService', function($scope, calcService){ $scope.a = 10; $scope.b = 20; $scope.doSum = function(){ //$scope.sum = calcService.getSum($scope.a, $scope.b); calcService.getSum($scope.a, $scope.b, function(r){ $scope.sum = r; }); }; }]); app.provider('calcService', function(){ var baseUrl = ''; this.config = function(url){ baseUrl = url; }; this.$get = ['$log', '$http', function($log, $http){ $log.log("instantiating calcService...") 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){ $http({ url: baseUrl + '/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; }]; }); app.config(['calcServiceProvider', function(calcServiceProvider){ calcServiceProvider.config("http://localhost:4467"); }]); |
最后,与上述任何服务一起工作的用户界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"> <script type="text/javascript" src="t03.js"> </head> <body ng-app="app"> Value of a is {{a}}, but you can change <input type=text ng-model="a" /> Value of b is {{b}}, but you can change <input type=text ng-model="b" /> Sum = {{sum}} <button ng-click="doSum()">Calculate</button> </body> </html> |
为了澄清问题,从AngularJS源代码中,您可以看到一个服务只是调用工厂函数,而工厂函数又调用提供者函数:
1 2 3 4 5 6 7 8 9 | function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } |
基于内存用途,只有在需要控制器时才会实例化控制器,而在不需要控制器时才会丢弃控制器。因此,每次切换路线或重新加载页面时,Angular都会清理当前控制器。然而,服务提供了一种在应用程序的生命周期中保留数据的方法,同时它们也可以以一致的方式跨不同的控制器使用。
Angular为我们提供了三种创建和注册自己服务的方法。
1)工厂
2)服务
3)提供者
工厂:工厂是一个简单的函数,它允许您在创建对象之前添加一些逻辑。它返回创建的对象。
它只是一个类的函数集合。因此,当您将它与构造函数函数一起使用时,可以在不同的控制器中实例化它。
服务:服务是使用new关键字创建对象的构造函数函数。可以使用此关键字向服务对象添加属性和函数。不像工厂,它什么也不退货。
它是一个单例对象。当需要在应用程序中共享单个对象时,可以使用它。例如,经过身份验证的用户详细信息。
提供者:提供者用于创建可配置的服务对象。它使用$get()函数返回值。
当您需要在使服务对象可用之前为其提供模块化配置时。
运行以下代码并查看输出。
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 29 30 31 32 33 34 35 36 37 | <!DOCTYPE html> <html ng-app="app"> <head> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.min.js"> <meta charset=utf-8 /> JS Bin </head> <body ng-controller="MyCtrl"> {{serviceOutput}} <br/><br/> {{factoryOutput}} <br/><br/> {{providerOutput}} var app = angular.module( 'app', [] ); var MyFunc = function() { this.name ="default name"; this.$get = function() { this.name ="new name" return"Hello from MyFunc.$get(). this.name =" + this.name; }; return"Hello from MyFunc(). this.name =" + this.name; }; // returns the actual function app.service( 'myService', MyFunc ); // returns the function's return value app.factory( 'myFactory', MyFunc ); // returns the output of the function's $get function app.provider( 'myProv', MyFunc ); function MyCtrl( $scope, myService, myFactory, myProv ) { $scope.serviceOutput ="myService =" + myService; $scope.factoryOutput ="myFactory =" + myFactory; $scope.providerOutput ="myProvider =" + myProv; } </body> </html> |
让我们简单地讨论一下AngularJS中处理业务逻辑的三种方法:(受Yaakov课程AngularJS的启发)
服务:
Syntax:
App.JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } } |
索引文件
1 | {{serviceExample.data}} |
服务特点:
工厂
首先,让我们看一下语法:
App.JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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; } |
现在在控制器中使用上述两个:
1 2 3 4 5 | var factoryOne = NameOfTheFactoryOne() //since it returns a function factoryOne.someMethod(); var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object factoryTwo.someMethod(); |
工厂特点:
供应商
让我们再看一看语法:
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 29 30 31 | 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; }; } } |
供应商特点:
本质上,提供者、工厂和服务都是服务。工厂是一个特殊的服务案例,当您所需要的只是$get()函数时,它允许您用更少的代码编写它。
服务、工厂和供应商之间的主要区别在于它们的复杂性。服务是最简单的形式,工厂有点健壮,提供者在运行时是可配置的。
以下是使用时间的摘要:
工厂:您提供的值需要根据其他数据计算。
服务:您返回的对象带有方法。
提供者:您希望能够在配置阶段配置在创建对象之前要创建的对象。在应用程序完全初始化之前,主要在应用程序配置中使用该提供程序。
工厂:在工厂内实际创建一个对象并将其返回的工厂。服务:您只有一个标准函数,它使用这个关键字来定义函数。提供程序:提供程序有一个$get,您可以定义它,它可以用来获取返回数据的对象。
1.服务是在必要时创建的单例对象,直到应用程序生命周期结束(浏览器关闭时)才被清除。当不再需要控制器时,它们会被销毁和清理。
2.创建服务的最简单方法是使用factory()方法。factory()方法允许我们通过返回包含服务函数和服务数据的对象来定义服务。服务定义函数是我们放置可注入服务的地方,例如$http和$q。前任:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; }); |
在我们的应用程序中使用factory()。
在我们的应用程序中使用工厂很容易,因为我们可以在运行时在需要的地方简单地注入工厂。
1 2 3 4 | angular.module('myApp') .controller('MainController', function($scope, User) { $scope.saveUser = User.save; }); |
不同的是句法糖。只需要提供程序。或者换句话说,只有提供者才是真正的角度,所有其他提供者都是派生的(为了减少代码)。还有一个简单的版本,叫做value(),它只返回值,不返回计算或函数。偶数值是从提供程序派生的!
那么,为什么会出现这样的复杂情况,为什么我们不能使用提供者而忘记其他的一切呢?它应该可以帮助我们轻松地编写代码,更好地进行通信。而厚着脸皮的回答是,框架越复杂,销售越好。
- 可以返回value=value的提供程序
- 一个可以实例化并返回=工厂(+value)
- 一个可以实例化+do something=service(+factory,+value)
- 提供者=必须包含名为$GET(+Factory、+Service、+Value)的属性
角注入为我们得出这个结论提供了第一个提示。
"$injector用于检索由提供程序定义的对象实例"不是服务,不是工厂,而是提供程序。
更好的答案是:"角度服务是由服务工厂创建的。这些服务工厂是由服务提供者依次创建的功能。服务提供者是构造函数函数。实例化时,它们必须包含一个名为$get的属性,该属性包含服务工厂函数。"
因此,主提供者和注入器以及所有这些都将就位:)。当$get可以通过从IServiceprovider继承在提供程序中实现时,typescript会变得有趣。