什么是之间的差异Service
,Provider
并Factory
在AngularJS?
service.factory
。不想进一步使这个主题复杂化。
什么是之间的差异Service
,Provider
并Factory
在AngularJS?
service.factory
。不想进一步使这个主题复杂化。
Answers:
从AngularJS邮件列表中,我得到了一个很棒的线程,它解释了服务,工厂,提供者及其注入用法。汇编答案:
语法: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的函数引用。
提供程序的优点是可以在模块配置阶段进行配置。
有关提供的代码,请参见此处。
这是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
负责创造价值的功能也是如此。注意,工厂函数可以要求其他依赖项。
但是,如果您想成为更多面向对象并开设名为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');
}
但这太罗y了。写这个的更短方法是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');
}
作为一个侧面说明,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;
});
};
toEqual
,并greeter.Greet
为。为什么不使用稍微真实和相关的内容?
factory
/ service
/的“ Hello world”示例provider
: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>
this
在$get
函数中更改上下文吗?-您不再在该函数中引用实例化的提供程序。
this
实际上并不会改变上下文,因为被调用的是new Provider()
。$ get(),Provider
函数被传递到app.provider
。就是说$get()
在构造上被称为方法Provider
,因此this
将参考Provider
实例建议。
Unknown provider: helloWorldProvider <- helloWorld
在本地运行时为什么会得到提示?注释掉,对于其他2个示例相同的错误。是否有一些隐藏的提供程序配置?(角度1.0.8)-找到:stackoverflow.com/questions/12339272/…–
TL; DR
1)使用工厂时,请创建一个对象,向其添加属性,然后返回该对象。当您将此工厂传递到控制器中时,对象上的那些属性现在将通过您的工厂在该控制器中可用。
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”上的那些属性现在将通过您的服务在该控制器上可用。
app.controller(‘myServiceCtrl’, function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service(‘myService’, function(){
var _artist = ‘Nelly’;
this.getArtist = function(){
return _artist;
}
});
3) 提供程序是您可以传递给.config()函数的唯一服务。当您想为服务对象提供模块范围的配置之前,请使用提供程序。
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)工厂
工厂是创建和配置服务的最流行方法。实际上,TL; DR所说的不多。您只需创建一个对象,向其添加属性,然后返回该对象即可。然后,当您将工厂传递到控制器中时,对象上的那些属性现在将通过工厂在该控制器中可用。下面是一个更广泛的示例。
app.factory(‘myFactory’, function(){
var service = {};
return service;
});
现在,当我们将“ myFactory”传递到控制器中时,我们附加到“服务”的任何属性都将可用。
现在,让我们在回调函数中添加一些“私有”变量。无法从控制器直接访问这些变量,但是我们最终将在“服务”上设置一些getter / setter方法,以便能够在需要时更改这些“私有”变量。
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;
});
在这里,您会注意到我们没有将这些变量/功能附加到“服务”上。我们只是创建它们,以便以后使用或修改它们。
现在我们的帮助器/私有变量和函数已经到位,让我们向“服务”对象添加一些属性。无论我们把“服务”放在哪儿,都可以在传递“ myFactory”的任何控制器中直接使用。
我们将创建仅返回或设置艺术家的setArtist和getArtist方法。我们还将创建一个方法,该方法将使用创建的URL调用iTunes API。此方法将返回一个诺言,一旦数据从iTunes API返回,诺言就会实现。如果您在AngularJS中使用诺言没有太多经验,我强烈建议您对它们进行深入研究。
在setArtist下方,接受一位艺术家,并允许您设置该艺术家。getArtist返回艺术家。callItunes首先调用makeUrl()来构建将用于$ http请求的URL。然后,它设置一个Promise对象,使用最终的URL发出$ 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()方法,并且仅在iTunes数据满足承诺后,才设置$ scope.data.artistData。您会注意到我们的控制器非常“薄”(这是一个很好的编码实践)。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。
2)服务
也许在创建服务时要知道的最大事情就是使用“ new”关键字实例化了它。对于您的JavaScript专家来说,这应该为您提供代码本质的一个重要提示。对于那些在JavaScript中具有有限背景的人或不太熟悉'new'关键字实际功能的人,让我们回顾一些JavaScript基础知识,这些基础知识最终将帮助我们理解服务的性质。
为了真正看到使用'new'关键字调用函数时发生的更改,让我们创建一个函数并使用'new'关键字调用它,然后让我们展示当解释器看到'new'关键字时所做的事情。最终结果将是相同的。
首先,让我们创建构造函数。
var Person = function(name, age){
this.name = name;
this.age = age;
}
这是典型的JavaScript构造函数。现在,每当我们使用“ new”关键字调用Person函数时,“ this”都将绑定到新创建的对象。
现在,让我们在Person的原型上添加一个方法,以便该方法在Person的“类”的每个实例上可用。
Person.prototype.sayName = function(){
alert(‘My name is ‘ + this.name);
}
现在,因为我们将sayName函数放在原型上,所以Person的每个实例都将能够调用sayName函数,以警告该实例的名称。
现在,我们在其原型上具有Person构造函数和sayName函数,让我们实际创建Person的实例,然后调用sayName函数。
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’
因此,所有用于创建Person构造函数,向其原型添加函数,创建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()时,解释器会说“确定,我将查看刚刚创建的'tyler'对象,找到sayName函数,然后对其进行调用。请稍等,我在这里看不到-我只看到名字和年龄,让我检查一下原型。是的,看起来像是在原型上,让我称呼它。”
下面的代码说明了如何思考'new'关键字在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中创建服务应该更容易理解。
创建服务时要了解的最大事情是知道服务已使用'new'关键字实例化。将这些知识与上面的示例相结合,您现在应该认识到,您将把属性和方法直接附加到“ this”,然后将从服务本身返回该属性和方法。让我们看看实际情况。
与我们最初对Factory示例所做的不同,我们不需要创建对象然后返回该对象,因为就像之前多次提到的那样,我们使用了'new'关键字,因此解释器将创建该对象并将其委托给它是原型,然后无需我们进行工作即可将其退还给我们。
首先,让我们创建我们的“私有”和帮助函数。这应该看起来非常熟悉,因为我们对工厂进行了完全相同的操作。我不会在这里解释每一行的功能,因为我在工厂示例中做了此操作,如果您感到困惑,请重新阅读工厂示例。
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;
}
});
现在就像在我们的工厂中一样,无论我们将myService传递给哪个控制器,都可以使用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部分中的唯一服务。如果您需要先更改服务对象的某些部分,然后再在应用程序中的其他任何地方使用它,则此功能至关重要。尽管与服务/工厂非常相似,但是我们将讨论一些区别。
首先,我们以与服务和工厂类似的方式设置提供商。下面的变量是我们的“私有”和助手功能。
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”服务传递到该特定控制器中时将在控制器中可用的所有变量/函数。
使用Provider创建服务时,控制器中唯一可用的属性/方法是从$ 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
}
}
现在完整的提供程序代码如下所示
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
}
}
});
现在就像在我们的工厂和服务中一样,无论将myProvider传递给哪个控制器,都可以使用setArtist,getArtist和callItunes。这是myProvider控制器(与我们的factory / Service控制器几乎完全相同)。
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函数更改某些变量。让我们来看一个例子。
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中时,它将是'此句子已设置...'。
所有服务都是单身人士 ; 每个应用实例化一次。它们可以是任何类型,无论是原语,对象文字,函数,还是自定义类型的实例。
的value
,factory
,service
,constant
,和provider
方法都是供应商。他们教注入者如何实例化服务。
提供者配方是最冗长但也是最全面的。在剩下的四个配方类型-价值,工厂,服务,不断- 是在运营商的配方上面只是语法糖。
仅当要公开必须在应用程序启动之前进行的应用程序范围配置的API时,才应使用提供者配方。这通常仅对可重复使用的服务感兴趣,这些服务的行为可能需要在应用程序之间稍有不同。
decorator
。了解AngularJS工厂,服务和提供者
所有这些都用于共享可重用的单例对象。它有助于在您的应用程序/各种组件/模块之间共享可重用的代码。
从文档服务/工厂:
- 懒惰地实例化 – Angular仅在应用程序组件依赖它时才实例化服务/工厂。
- 单例 –依赖于服务的每个组件都将引用由服务工厂生成的单个实例。
工厂是一种函数,您可以在创建对象之前操纵/添加逻辑,然后返回新创建的对象。
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”关键字实例化新对象的功能。您可以使用this
关键字将属性和功能添加到服务对象。与工厂不同,它不返回任何内容(它返回包含方法/属性的对象)。
app.service('MyService', function() {
//directly binding events to this context
this.myServiceFunction = function() {
//TO DO:
};
});
用法
需要在整个应用程序中共享单个对象时使用它。例如,经过身份验证的用户详细信息,可共享的方法/数据,实用程序功能等。
提供程序用于创建可配置的服务对象。您可以从配置功能配置服务设置。它使用该$get()
函数返回一个值。该$get
函数在运行阶段以角度执行。
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.
};
};
});
用法
需要在使服务对象可用之前为它提供模块方式的配置时,例如。假设你要设置你的API URL您的环境像的基础dev
,stage
或prod
注意
在角度配置阶段仅提供者可用,而服务和工厂则不可用。
希望这能清除您对Factory,Service和Provider的了解。
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
,所以听起来不可能,对吧?
对我来说,当我意识到它们都以相同的方式工作时,就得到了启示:通过运行一次,存储它们获得的值,然后在通过依赖注入进行引用时咳出相同的存储值。
说我们有:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
两者之间的区别在于:
a
的储值来自跑步 fn
。b
的储值来自new
ingfn
。c
的存储值来自首先通过new
ing 获取实例fn
,然后运行$get
该实例的方法。这意味着在AngularJS中有一个类似于缓存对象的对象,其每次注入的值仅在第一次注入时才分配一次,其中:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
这就是为什么我们this
在服务中使用并定义this.$get
in提供程序的原因。
factory
s 做得很好。service
存在的唯一原因是诸如CoffeeScript,TypeScript,ES6等语言,因此您可以使用它们的类语法。provider
仅当您的模块用于多个具有不同设置的应用程序中时,才需要使用app.config()
。如果您的服务是纯单例或能够创建某些实例,则仅取决于您的实现。
服务vs提供者vs工厂:
我试图保持简单。这都是关于基本JavaScript概念的。
首先,让我们谈谈AngularJS中的服务!
什么是服务: 在AngularJS中,服务只是一个可以存储一些有用方法或属性的JavaScript对象。此单例对象是基于ngApp(Angular应用)创建的,并且在当前应用内的所有控制器之间共享。Angularjs实例化服务对象时,它将使用唯一的服务名称注册该服务对象。因此,每当我们需要服务实例时,Angular都会在注册表中搜索该服务名称,然后它将对服务对象的引用返回。这样我们就可以在服务对象上调用方法,访问属性等。您可能会质疑是否还可以将属性,方法放在控制器的作用域对象上!那么为什么需要服务对象?答案是:服务在多个控制器范围之间共享。如果将某些属性/方法放在控制器的作用域对象中,则该属性/方法仅对当前作用域可用。
因此,如果存在三个控制器范围,即controllerA,controllerB和controllerC,它们将共享同一服务实例。
<div ng-controller='controllerA'>
<!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
<!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
<!-- controllerC scope -->
</div>
如何创建服务?
AngularJS提供了不同的方法来注册服务。在这里,我们将集中讨论三种方法factory(..),service(..),provider(..);
我们可以如下定义一个工厂函数。
factory('serviceName',function fnFactory(){ return serviceInstance;})
AngularJS提供了'factory('serviceName',fnFactory)'方法,该方法带有两个参数,serviceName和一个JavaScript函数。Angular通过调用如下函数fnFactory()创建服务实例。
var serviceInstace = fnFactory();
传递的函数可以定义一个对象并返回该对象。AngularJS只是将该对象引用存储到作为第一个参数传递的变量中。从fnFactory返回的任何内容都将绑定到serviceInstance。除了返回object之外,我们还可以返回函数,值等,无论返回什么内容,都可以用于服务实例。
例:
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('serviceName',function fnServiceConstructor(){})
这是另一种方式,我们可以注册服务。唯一的区别是AngularJS尝试实例化服务对象的方式。这次angular使用了'new'关键字,并调用构造函数,如下所示。
var serviceInstance = new fnServiceConstructor();
在构造函数中,我们可以使用'this'关键字为服务对象添加属性/方法。例:
//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()函数,我们可以创建可配置的服务。
我们可以使用提供者语法创建可配置服务,如下所示。
/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});
/*step2:configure the service */
app.config(function configureService(serviceProvider){});
1.Provider对象是使用我们在provider函数中定义的构造函数创建的。
var serviceProvider = new serviceProviderConstructor();
2.我们传入app.config()的函数被执行。这称为配置阶段,在这里我们有机会自定义我们的服务。
configureService(serviceProvider);
3.最后,通过调用serviceProvider的$ get方法创建服务实例。
serviceInstance = serviceProvider.$get()
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();
服务使用构造函数,Angular使用“ new”关键字调用此构造函数以创建服务实例。 serviceInstance =新的fnServiceConstructor();
Provider定义了providerConstructor函数,此providerConstructor函数定义了工厂函数$ get。Angular调用$ get()创建服务对象。提供程序语法具有在实例化服务对象之前对其进行配置的另一个优点。 serviceInstance = $ get();
正如这里的几个人所正确指出的,工厂,提供者,服务,甚至价值和常数都是同一事物的版本。您可以将更一般的内容provider
分解为所有内容。像这样:
这是该图片的来源文章:
您为AngularJS提供了一个函数,当请求工厂时,AngularJS将缓存并注入返回值。
例:
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;
});
您给AngularJS一个函数,AngularJS将调用new来实例化它。AngularJS创建的实例将在请求服务时进行缓存和注入。由于使用new来实例化服务,因此关键字this是有效的并引用实例。
例:
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();
});
您给AngularJS一个函数,AngularJS将调用它的$get
函数。$get
当请求服务时,将缓存并注入该函数的返回值。
提供程序允许您在 AngularJS调用$get
方法以获取注射剂之前配置提供程序。
例:
app.provider('provider', function() {
var name = '';
this.setName = function(newName) {
name = newName;
}
this.$get = function() {
return {
name: name
}
}
})
用法(作为控制器中的注射剂)
app.controller('ctrl', function($scope, provider) {
$scope.name = provider.name;
});
用法(在$get
调用提供者之前配置提供程序以创建可注射对象)
app.config(function(providerProvider) {
providerProvider.setName('John');
});
与提供者一起玩时,我注意到一些有趣的事情。
供应商的注射剂可见性与服务和工厂的可见性不同。如果您声明AngularJS为“常数”(例如myApp.constant('a', 'Robert');
),则可以将其注入服务,工厂和提供者。
但是,如果您声明AngularJS“值”(例如。myApp.value('b', {name: 'Jones'});
),则可以将其注入服务和工厂,但不能注入提供者创建函数。但是,您可以将其注入$get
您为提供程序定义的函数中。AngularJS文档中提到了这一点,但很容易遗漏。您可以在%provide页面上的value和常量方法部分中找到它。
<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>
对于新手来说,这是非常令人困惑的部分,我已尝试用简单的词来澄清它
AngularJS服务:用于与控制器中的服务引用共享实用程序功能。服务本质上是单例的,因此对于一项服务,在浏览器中仅创建一个实例,并且在整个页面中使用相同的引用。
在服务中,我们使用此对象将函数名称创建为属性。
AngularJS Factory:Factory的目的也与Service相同,但是在这种情况下,我们创建一个新对象并添加函数作为该对象的属性,最后返回该对象。
AngularJS提供程序:的目的还是相同的,但是Provider给出了$ get函数的输出。
在http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider中解释和定义了服务,工厂和提供者
对我而言,了解差异的最好和最简单的方法是:
var service, factory;
service = factory = function(injection) {}
AngularJS如何实例化特定组件(简化):
// service
var angularService = new service(injection);
// factory
var angularFactory = factory(injection);
因此,对于服务而言,变成AngularJS组件的是由服务声明函数表示的类的对象实例。对于工厂,它是从工厂声明函数返回的结果。工厂的行为可能与服务相同:
var factoryAsService = function(injection) {
return new function(injection) {
// Service content
}
}
最简单的思维方式是:
注释中提供了工厂“类”示例,以及提供程序差异。
new MyService()
:)
我对此事的澄清:
基本上所有提到的类型(服务,工厂,提供者等)都只是在创建和配置全局变量(对于整个应用程序当然是全局的),就像老式的全局变量一样。
尽管不建议使用全局变量,但这些全局变量的实际用法是通过将变量传递给相关控制器来提供依赖项注入。
创建“全局变量”的值有很多复杂程度:
app.config
app.config
上面的方法是,它的返回值用于初始化“全局”变量。 我的理解很简单。
工厂: 您只需在工厂内部创建一个对象并将其返回即可。
服务:
您只有一个使用此关键字定义功能的标准功能。
提供者:
$get
您定义了一个对象,它可用于获取返回数据的对象。
Angular文档的摘要:
SO的最佳答案:
https://stackoverflow.com/a/26924234/165673(<- 良好)
https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673
所有的好答案已经。我想在Service和Factory上添加更多点。以及服务/工厂之间的差异。还有一个问题,例如:
让我们从服务和工厂之间的区别开始:
两者都是Singletons:每当Angular第一次将它们作为依赖项时,它都会创建服务/工厂的单个实例。创建实例后,将永远使用同一实例。
可用于对具有行为的对象进行建模:它们都可以具有方法,内部状态变量等。尽管您编写该代码的方式会有所不同。
服务:
服务是构造函数,Angular将通过调用new实例化它yourServiceName()
。这意味着几件事情。
this
。new yourServiceName(
)时,它将收到this
具有您放置在其上的所有属性的对象。范例范例:
angular.service('MyService', function() {
this.aServiceVariable = "Ved Prakash"
this.aServiceMethod = function() {
return //code
};
});
当Angular将此
MyService
服务注入依赖它的控制器时,该控制器将获得一个MyService
可以在其上调用函数的函数,例如MyService.aServiceMethod()。
请注意this
:
由于构造的服务是一个对象,因此调用它时,其中的方法可以引用此对象:
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++;
};
});
您可能会想调用ScoreKeeper.setScore
一个Promise链,例如,如果您是通过从服务器获取分数来初始化分数的,则$http.get('/score').then(ScoreKeeper.setScore).
该问题是:绑定时ScoreKeeper.setScore
会调用this
to,null
并且会出错。更好的方法是$http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))
。无论您是否选择在服务方法中使用此功能,请谨慎使用它们。
从中返回值Service
:
由于JavaScript构造函数的工作方式,如果您(i.e., an Object)
从constructor
函数返回复杂值,则调用者将获取该Object而不是this实例。
这意味着您基本上可以从下面复制粘贴工厂示例,替换factory
为service
,它将起作用:
angular.service('MyService', function($http) {
var api = {};
api.aServiceMethod= function() {
return $http.get('/users');
};
return api;
});
因此,当Angular使用新的MyService()构造您的服务时,它将获得该api对象而不是MyService实例。
这是任何复杂值(对象,函数)的行为,但不是原始类型的行为。
工厂:
工厂是返回值的普通旧函数。返回值是注入到取决于工厂的事物中的东西。Angular中典型的工厂模式是返回一个具有函数作为属性的对象,如下所示:
angular.factory('MyFactory', function($http) {
var api = {};
api.aFactoryMethod= function() {
return $http.get('/users');
};
return api;
});
工厂依赖项的注入值是工厂的返回值,并且不必是对象。这可能是一个功能
以上1和2个问题的答案:
在大多数情况下,只需坚持使用工厂进行所有操作即可。他们的行为更容易理解。没有选择是否返回值,而且,如果做错了事,也不会引入任何错误。
但是,当我谈论将它们作为依赖项注入时,我仍将它们称为“服务”。
服务/工厂的行为非常相似,有些人会说任何一个都可以。确实有些道理,但我发现遵循约翰·帕帕(John Papa)的风格指南的建议更容易,而且坚持使用工厂即可。**
另外需要说明的是,工厂可以创建函数/基元,而服务不能。看看这个的jsfiddle基于Epokk的:http://jsfiddle.net/skeller88/PxdSP/1351/。
工厂返回可以调用的函数:
myApp.factory('helloWorldFromFactory', function() {
return function() {
return "Hello, World!";
};
});
工厂还可以使用可以调用的方法返回对象:
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!";
}
};
});
该服务返回一个对象,该对象具有可以调用的方法:
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!";
};
});
有关更多详细信息,请参阅我撰写的有关差异的文章:http : //www.shanemkeller.com/tldr-services-vs-factories-in-angular/
已经有很好的答案,但是我只想分享这个。
首先:提供者是创建广告的方式/方式service
(单个对象)假定由$ injector注入(AngulaJS如何处理IoC模式)。
和价值,工厂,服务和常量(4种方式)- 提供者方式/收入之上的语法糖。
有Service vs Factory
一部分已被覆盖:https :
//www.youtube.com/watch?v=BLzNCkPn3ao
服务new
实际上是关于关键字的,我们知道它做4件事:
prototype
对象context
到this
this
而Factory就是关于Factory Pattern的-包含返回诸如Service之类的对象的函数。
这个简单/简短的视频:还介绍了提供者:https : //www.youtube.com/watch?v=HvTZbQ_hUZY(您可以看到他们如何从工厂转到提供者)
在应用程序已完全启动/初始化之前,提供者配方主要用于应用程序配置中。
在阅读了所有这些帖子之后,它为我带来了更多混乱。.但是仍然所有有价值的信息..最后,我找到了下表,该表将通过简单的比较提供信息
对于初学者来说,这可能不是正确的用例,但总的来说,这是这三个用例的用例。
angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})
对于基本方案,factory&Service的行为相同。
这是一些我为AngularjS中的对象工厂编写的代码模板。我以Car / CarFactory为例进行说明。在控制器中编写简单的实现代码。
<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>
这是一个简单的例子。我正在使用一些第三方库,它们期望通过不同的对象属性显示“纬度”和“经度”的“位置”对象。我不想破解供应商代码,所以我调整了我传递的“位置”对象。
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
;
将此页面和文档(自上次浏览以来,似乎有了很大的改进)作为参考,我整理了以下真实(-ish)世界演示,该演示使用了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>
应用程式
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);
};
});
工作演示。
该答案解决了主题/问题
要么
基本上会发生什么
当你犯了一个factory()
它设置你function
的第二个参数提供给供应商$get
,并返回它(provider(name, {$get:factoryFn })
),你得到的是provider
,但有总比没有其他属性/方法$get
的是provider
(意味着你可以不配置此)
工厂源代码
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
};
进行service()
返回时,您需要提供一个factory()function
来注入constructor
(返回您在服务中提供的构造函数的实例)并返回
服务源代码
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
};
因此,基本上在两种情况下,您最终都会将提供程序$ get设置为所提供的函数,但是除了$ 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;
...
晚会晚了。但是我认为这对于希望学习(或弄清楚)使用工厂,服务和提供者方法开发Angular JS定制服务的人来说更有用。
我看了这段视频,清楚地说明了开发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");
});
};
}]);
定制服务的“提供者”方法的代码(如果您要开发可以配置的服务,则这是必需的):
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源中看到一个服务仅调用了工厂函数,而工厂函数又调用了提供程序函数:
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
让我们以一种简单的方式来讨论在AngularJS中处理业务逻辑的三种方式:(受Yaakov的Coursera AngularJS课程的启发)
服务内容:
句法:
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;
};
}
}
提供者的特征:
.service
或.factory
方法配置服务时,provider方法实际上是在后台执行的。$get
是直接连接到提供程序实例的功能。该功能是出厂功能。换句话说,就像我们用来提供给该.factory
方法的那个一样。在该功能中,我们创建了自己的服务。该$get
属性即函数,是提供者成为提供者的原因。AngularJS期望提供程序具有$ get属性,其值是Angular将其视为工厂函数的函数。但是,使整个提供程序设置非常特别的原因是,我们可以config
在服务提供程序内提供一些对象,并且通常带有默认值,我们以后可以在配置配置整个应用程序的步骤中覆盖这些默认值。本质上,提供者,工厂和服务都是服务。当您需要的只是一个$ 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()
在我们的应用程序中使用工厂很容易,因为我们可以在运行时将其简单地注入到需要的地方。
angular.module('myApp')
.controller('MainController', function($scope, User) {
$scope.saveUser = User.save;
});
语法糖是与众不同的。只需要提供者。或者换句话说,只有提供者是真正的角度,所有其他提供者都是派生的(以减少代码)。还有一个简单的版本,称为Value(),它仅返回值,不返回计算或函数。价值甚至来自提供者!
那么,为什么会有这样的复杂性,为什么我们不能仅仅使用provider却忘记了其他一切呢?它应该可以帮助我们轻松编写代码并更好地沟通。面面俱到的回答是,它变得越复杂,框架就越畅销。
角注入为我们提供了得出这一结论的第一个暗示。
“ $ injector用于检索由提供程序定义的对象实例”,而不是服务,不是工厂而是提供程序。
更好的答案是:“ Angular服务是由服务工厂创建的。这些服务工厂是由服务提供者创建的函数。服务提供者是构造函数。实例化时,它们必须包含一个属性。称为$ get,其中包含服务工厂功能。”
因此,主提供方和注入方都将落在原处:)。当可以通过从IServiceProvider继承在提供程序中实现$ get时,它在Typescript中变得很有趣。