AngularJS中的服务、提供商和工厂之间有什么区别?
当前回答
从AngularJS邮件列表中,我得到了一个令人惊叹的线程,它解释了服务与工厂与供应商以及它们的注入用法。编写答案:
服务
语法:module.service('serviceName',function);结果:当将serviceName声明为可注入参数时,将为您提供函数的实例。换句话说,新的FunctionYouPassedToService()。
工厂
语法:module.factory('factoryName',function);结果:当将factoryName声明为可注入参数时,将提供通过调用传递给module.factory的函数引用返回的值。
提供商
语法:module.provider('providerName',function);结果:当将providerName声明为可注入参数时,将提供(newProviderFunction())$get()。在调用$get方法之前实例化构造函数函数-ProviderFunction是传递给module.provider的函数引用。
提供程序的优点是可以在模块配置阶段进行配置。
请参阅此处获取提供的代码。
下面是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);
}
因此,工厂是一个负责创造价值的职能部门。注意,工厂函数可以要求其他依赖项。
但是,如果你想成为更多的OO,并拥有一个名为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);
但是如果我们想在注入之前配置Greeter类呢?然后我们可以写
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');
}
顺便说一句,服务、工厂和价值都来自提供者。
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;
});
};
其他回答
看完所有这些帖子后,这给我带来了更多的困惑……但所有的信息都是有价值的。。最后我找到了下表,它将通过简单的比较提供信息
注射器使用配方创建两种类型的对象:服务和特殊用途物体有五种配方类型定义了如何创建对象:Value,工厂、服务、供应商和常量。工厂和服务是最常用的配方。它们之间的唯一区别是,Service配方对自定义类型的对象更有效,而Factory可以生成JavaScript原语和函数。提供者配方是核心配方类型,所有其他配方都只是其语法上的糖。提供程序是最复杂的配方类型。除非您正在构建一段需要全局配置的可重用代码,否则您不需要它。除控制器外的所有特殊用途对象均通过工厂配方定义。
对于初学者来说:-这可能不正确的用例,但在高级别上,这是这三个用例的用例。
如果要在angular模块中使用,应将配置函数创建为提供程序
angular.module('myApp').config(函数($testProvider){$testProvider.someFunction();})
Ajax调用或第三方集成需要提供服务。对于数据操作,将其创建为工厂
对于基本场景,工厂和服务的行为相同。
我对此事的澄清:
基本上,所有提到的类型(服务、工厂、提供者等)都只是创建和配置全局变量(当然,这些变量对整个应用程序是全局的),就像老式的全局变量一样。
虽然不建议使用全局变量,但这些全局变量的实际用途是通过将变量传递给相关控制器来提供依赖注入。
在为“全局变量”创建值时有许多复杂程度:
常数这定义了一个在整个应用程序中不应修改的实际常量,就像其他语言中的常量一样(JavaScript缺少的)。价值这是一个可修改的值或对象,它充当一些全局变量,甚至可以在创建其他服务或工厂时注入(请参阅有关这些的详细信息)。然而,它必须是一个“文字值”,这意味着必须写出实际值,并且不能使用任何计算或编程逻辑(换句话说,39或myText或{prop:“value”}可以,但2+2不行)。工厂一个更一般的值,可以立即计算。它的工作原理是将一个函数传递给AngularJS,其中包含计算值所需的逻辑,AngularJS执行该函数,并将返回值保存在命名变量中。请注意,可以返回对象(在这种情况下,它的功能类似于服务)或函数(将作为回调函数保存在变量中)。服务服务是工厂的更精简版本,只有当值是对象时才有效,它允许直接在函数中写入任何逻辑(就像它是构造函数一样),以及使用this关键字声明和访问对象财产。供应商与工厂的简化版本的服务不同,提供程序是一种更复杂但更灵活的初始化“全局”变量的方法,最大的灵活性是从app.config中设置值的选项。它的工作方式类似于使用服务和提供者的组合,方法是向提供者传递一个函数,该函数具有使用this关键字声明的财产,该关键字可以从app.config中使用。然后它需要有一个单独的$.get函数,该函数由AngularJS在通过app.config文件设置上述财产后执行,该$.get功能的行为与上述工厂相同,因为它的返回值用于初始化“全局”变量。
让我们以简单的方式讨论AngularJS中处理业务逻辑的三种方法:(灵感来自Yaakov的Coursera AngularJS课程)
服务:
语法:
应用程序.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;
}
}
索引html
<div ng-controller = "ServiceExampleController as serviceExample">
{{serviceExample.data}}
</div>
服务特点:
懒惰实例化:如果它没有被注入,就永远不会被实例化。因此,要使用它,必须将其注入到模块中。Singleton:如果注入到多个模块,所有模块都只能访问一个特定的实例。这就是为什么在不同控制器之间共享数据非常方便的原因。
工厂
首先让我们看看语法:
应用.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是在Angular中创建服务的最灵活的方法。我们不仅可以创建一个可动态配置的工厂,而且在使用工厂时,通过提供程序方法,我们可以在整个应用程序启动时自定义配置工厂一次。然后,工厂可以在整个应用程序中使用自定义设置。换句话说,我们可以在应用程序启动之前配置此工厂。事实上,在angular文档中提到,当我们使用.service或.factory方法配置服务时,提供者方法实际上是在幕后执行的。$get是一个直接附加到提供程序实例的函数。该函数是工厂函数。换句话说,它就像我们用来提供给.factory方法的一样。在该函数中,我们创建自己的服务。这个$get属性是一个函数,它使提供者成为提供者。AngularJS希望提供程序具有$get属性,该属性的值是Angular将作为工厂函数处理的函数。但使整个提供程序设置非常特殊的是,我们可以在服务提供程序内部提供一些配置对象,这通常带有默认值,我们可以稍后在步骤中覆盖这些默认值,在该步骤中我们可以配置整个应用程序。
派对有点晚了。但我认为这对那些想学习(或清楚)如何使用工厂、服务和提供者方法开发Angular JS自定义服务的人更有用。
我看到了这段视频,它清楚地解释了开发AngularJS定制服务的工厂、服务和供应商方法:
https://www.youtube.com/watch?v=oUXku28ex-米
源代码:http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service
此处发布的代码直接从上述来源复制,以使读者受益。
基于“工厂”的定制服务的代码如下(它与同步和异步版本一起调用http服务):
var app=角度模块(“app”,[]);app.controller('imp',['$scope','calcFactory',函数($scope,calcFactory){$scope.a=10;$scope.b=20;$scope.doSum=函数(){//$scope.sum=calcFactory.getSum($scope.a,$scope.b)//同步的calcFactory.getSum($scope.a,$scope.b,函数(r){//aynchronous$scope.sum=r;});};}]);app.factory('calFactory',['$http','$log',函数($http,$log){$log.log(“实例化calcFactory.”);var oCalcService={};//oCalcService.getSum=函数(a,b){//返回parseInt(a)+parseInt(b);//};//oCalcService.getSum=函数(a,b,cb){//var s=parseInt(a)+parseInt(b);//cb(s);//};oCalcService.getSum=函数(a,b,cb){//使用http服务$http({url:'http://localhost:4467/Sum?a='+a+'和b='+b,方法:'GET'}).然后(函数(resp){$log.log(对应数据);cb(相应数据);},函数(resp){$log.error(“出现错误”);});};return oCalcService;}]);
自定义服务的“服务”方法的代码(这与“工厂”非常相似,但从语法角度来看不同):
var app=角度模块(“app”,[]);app.controller('imp',['$scope','calcService',函数($scope,calcService){$scope.a=10;$scope.b=20;$scope.doSum=函数(){//$scope.sum=calcService.getSum($scope.a,$scope.b);calcService.getSum($scope.a,$scope.b,函数(r){$scope.sum=r;}); };}]);app.service('calService',['$http','$log',函数($http,$log){$log.log(“实例化calcService..”);//this.getSum=函数(a,b){//返回parseInt(a)+parseInt(b);//};//this.getSum=函数(a,b,cb){//var s=parseInt(a)+parseInt(b);//cb(s);//};this.getSum=函数(a,b,cb){$http({url:'http://localhost:4467/Sum?a='+a+'和b='+b,方法:'GET'}).然后(函数(resp){$log.log(对应数据);cb(相应数据);},函数(resp){$log.error(“出现错误”);});};}]);
自定义服务的“提供者”方法的代码(如果您想开发可配置的服务,这是必要的):
var app=角度模块(“app”,[]);app.controller('imp',['$scope','calcService',函数($scope,calcService){$scope.a=10;$scope.b=20;$scope.doSum=函数(){//$scope.sum=calcService.getSum($scope.a,$scope.b);calcService.getSum($scope.a,$scope.b,函数(r){$scope.sum=r;}); };}]);app.provider('calService',函数(){var baseUrl=“”;this.config=函数(url){baseUrl=url;};这一点$get=['$log','$http',函数($log,$http){$log.log(“正在实例化calcService…”)var oCalcService={};//oCalcService.getSum=函数(a,b){//返回parseInt(a)+parseInt(b);//};//oCalcService.getSum=函数(a,b,cb){//var s=parseInt(a)+parseInt(b);//cb(s); //};oCalcService.getSum=函数(a,b,cb){$http({url:baseUrl+'/Sum?a=‘+a+’&b=‘+b,方法:'GET'}).然后(函数(resp){$log.log(对应数据);cb(相应数据);},函数(resp){$log.error(“出现错误”);});}; return oCalcService;}];});app.config(['calServiceProvider',函数(calServiceProvider){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>a的值为{{a}},但你可以改变<input-type=text-ng-model=“a”/><br>b的值是{{b}},但你可以改变<input-type=text-ng-model=“b”/><br></div>总和={{Sum}}<br><button ng click=“doSum()”>计算</button></div></body></html>
使用此页面和文档作为参考(自上次查看以来似乎有了很大的改进),我整理了以下真实世界演示,其中使用了提供者的5种风格中的4种;价值、常量、工厂和全面供应商。
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);
};
});
工作演示。
推荐文章
- 如何设置一个iframe src属性从一个变量在AngularJS
- 如何在Angular中使用$rootScope来存储变量?
- AngularJS:工厂和服务?
- 父ng-repeat从子ng-repeat的访问索引
- 无法解析类型为“Microsoft.AspNetCore.Http.IHttpContextAccessor”的服务
- AngularJS使用ng-class切换类
- 如何在Angular.js中配置不同的环境?
- 当使用ng-model时,输入文本框上的Value属性被忽略?
- 使用AngularJS跟踪谷歌分析页面视图
- 传递参数
- 在“DOMWindow”上执行“postMessage”失败:https://www.youtube.com !== http://localhost:9000
- Angular中相当于AngularJS $watch的东西是什么?
- AngularJS路由不带散列“#”
- 单击表单中的按钮会刷新页面
- 典型的AngularJS工作流程和项目结构(使用Python Flask)