Service
、Provider
和Factory
有什么区别?Service
、Provider
和Factory
有什么区别?<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>
应用程序
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);
};
});
工作中demo。
这篇答案回答了如何将工厂、服务和常量视为提供程序配方的语法糖的问题。
或者
工厂、服务和提供程序在内部的相似之处是什么。
基本上发生的事情是,当你创建一个factory()
时,它将第二个参数中提供的function
设置为提供程序的$get
并返回它(provider(name, {$get:factoryFn})
),所有你得到的只是provider
,但该provider
除了$get
方法/属性之外没有其他属性/方法(也就是说,你无法配置它)。
factory源代码
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
};
当创建一个service()
时,它将通过提供一个带有function
的factory()
来注入constructor
(返回您在服务中提供的构造函数的实例)并返回它。
服务的源代码
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
};
基本上,在这两种情况下,您最终都会将提供程序的 $get 设置为您提供的函数,但是您可以在 provider() 的配置块中最初提供任何额外的东西。
我知道很多出色的答案,但我必须分享一下我使用的经验:
1. 对于大多数情况下的默认选项,我使用service
2. 使用factory
创建特定实例的服务
// 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){ ...
}
}
})();
并使用:
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源代码中,您可以看到一个服务只是调用工厂函数,而工厂函数反过来又调用提供程序函数:
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
虽然我来晚了,但我认为这对那些想学习(或明确)使用工厂、服务和提供程序方法开发AngularJS自定义服务的人更有帮助。
我找到了这个视频,清楚地解释了使用工厂、服务和提供程序方法开发AngularJS自定义服务:
https://www.youtube.com/watch?v=oUXku28ex-M
源代码:http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service
此处发布的代码直接从上述来源复制而来,以使读者受益。
基于“工厂”模式的自定义服务代码如下(包括同步和异步版本以及调用http服务):
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;
}
]);
"服务"方法的代码,用于自定义服务(这与“工厂”非常相似,但从语法角度来看不同):
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");
});
};
}]);
"provider" 方法适用于定制服务的代码(如果您想开发可配置的服务,则这是必需的):
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");
}]);
最后,该UI可适用于上述任何服务:
<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
<div ng-controller="emp">
<div>
Value of a is {{a}},
but you can change
<input type=text ng-model="a" /> <br>
Value of b is {{b}},
but you can change
<input type=text ng-model="b" /> <br>
</div>
Sum = {{sum}}<br>
<button ng-click="doSum()">Calculate</button>
</div>
</body>
</html>
让我们简单地讨论 AngularJS 中处理业务逻辑的三种方式:(受 Yaakov 的 Coursera AngularJS 课程启发)
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>
服务的特点:
工厂模式
首先让我们看一下语法:
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;
}
现在在控制器中使用上述两个内容:
var factoryOne = NameOfTheFactoryOne() //since it returns a function
factoryOne.someMethod();
var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
factoryTwo.someMethod();
工厂的特点:
.service()
方法是一个工厂,总是产生同一类型的服务,即单例,并且没有任何简单的方式来配置它的行为。该 .service()
方法通常被用作不需要任何配置的快捷方式。提供者
让我们先看一下语法:
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;
};
}
}
Provider的特点:
工厂: 工厂是创建对象并返回它的实际位置。
服务: 服务只是一个使用this关键字定义函数的标准函数。
提供者: 提供者有一个 $get,您可以定义并将其用于获取返回数据的对象。
1.服务是单例对象,它们在必要时创建,并且直到应用程序生命周期结束(当浏览器关闭时)才被清除。控制器在不再需要时被销毁和清理。
2.使用factory()方法是创建服务的最简单方法。 factory()方法允许我们通过返回包含服务函数和服务数据的对象来定义服务。服务定义函数是我们放置可注入服务(如$http和$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; });
在我们的应用程序中使用factory()函数
由于我们可以在运行时简单地将它注入到需要的位置,因此在我们的应用程序中使用factory非常容易。
angular.module('myApp')
.controller('MainController', function($scope, User) {
$scope.saveUser = User.save;
});
语法糖就在于其中的差异。只需要提供者。换句话说,只有提供者才是真正的Angular,其他所有的都是派生出来的(为了减少代码)。还有一个简单版本,叫做Value(),它只返回值,没有计算或函数。即使Value也是从提供者派生出来的!
那么为什么会有这样的复杂性,为什么我们不能只使用提供者并忘记其他一切呢?它应该帮助我们更轻松地编写代码和更好地沟通。而玩笑式的回答是,它变得越复杂,框架的销售就会越好。
Angular注入提供了我们达成这个结论的第一个提示。
"$injector用于检索由提供者定义的对象实例"而不是服务或工厂,而是提供者。
更好的答案是: “Angular服务是由服务工厂创建的。这些服务工厂是函数,又由服务提供者创建。服务提供者是构造函数。当被实例化时,它们必须包含一个名为 $get 的属性,其中保存了服务工厂函数。”
所以精通提供者和注入器,一切都会落在适当的位置:)。而在TypeScript中,当通过继承IServiceProvider来实现$get时,情况变得更有趣。
service.factory
成功处理。我不想进一步复杂化这个主题。 - demisxservices
、factories
和providers
的好答案。 - Mistalis