Service
とAngularJSProvider
の違いは何ですか?Factory
30 に答える
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
の作成を担当する関数も同様です。ファクトリ関数は他の依存関係を要求できることに注意してください。
しかし、もっと 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');
}
補足として、service
、factory
、およびvalue
はすべてプロバイダから派生しています。
provider.service = function(name, Class) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.instantiate(Class);
};
});
}
provider.factory = function(name, factory) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.invoke(factory);
};
});
}
provider.value = function(name, value) {
provider.factory(name, function() {
return value;
});
};
JS フィドルのデモ
factory
/ service
/を使用した「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>
TL;DR
1) Factoryを使用している場合は、オブジェクトを作成し、それにプロパティを追加してから、同じオブジェクトを返します。このファクトリをコントローラに渡すと、オブジェクトのこれらのプロパティは、ファクトリを介してそのコントローラで使用できるようになります。
app.controller(‘myFactoryCtrl’, function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory(‘myFactory’, function(){
var _artist = ‘Shakira’;
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) Serviceを使用している場合、AngularJS は「new」キーワードを使用して舞台裏でそれをインスタンス化します。そのため、'this' にプロパティを追加すると、サービスは 'this' を返します。サービスをコントローラーに渡すと、「this」のこれらのプロパティは、サービスを通じてそのコントローラーで使用できるようになります。
app.controller(‘myServiceCtrl’, function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service(‘myService’, function(){
var _artist = ‘Nelly’;
this.getArtist = function(){
return _artist;
}
});
3) プロバイダーは、.config() 関数に渡すことができる唯一のサービスです。サービス オブジェクトを使用可能にする前に、モジュール全体の構成を提供する場合は、プロバイダーを使用します。
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' をコントローラーに渡すと、'service' にアタッチしたすべてのプロパティが使用可能になります。
それでは、コールバック関数に「プライベート」変数をいくつか追加しましょう。これらはコントローラーから直接アクセスできませんが、必要に応じてこれらの「プライベート」変数を変更できるように、最終的に「サービス」にいくつかのゲッター/セッター メソッドを設定します。
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;
});
ここで、これらの変数/関数を「サービス」にアタッチしていないことに気付くでしょう。後で使用または変更するために作成しているだけです。
- baseUrl は、iTunes API が必要とするベース URL です。
- _artist は検索したいアーティストです
- _finalUrl は、iTunes への呼び出しを行う最終的な完全に構築された URL です。
- makeUrl は、iTunes フレンドリー URL を作成して返す関数です。
ヘルパー/プライベート変数と関数が配置されたので、いくつかのプロパティを「サービス」オブジェクトに追加しましょう。「サービス」に配置したものはすべて、「myFactory」を渡すコントローラー内で直接使用できます。
単純にアーティストを返すか設定する setArtist および getArtist メソッドを作成します。作成した URL で iTunes API を呼び出すメソッドも作成します。このメソッドは、データが iTunes API から返されたときに満たされる promise を返します。AngularJS で promise を使用した経験があまりない場合は、promise について深く掘り下げることを強くお勧めします。
以下のsetArtistはアーティストを受け入れ、アーティストを設定できるようにします。getArtistはアーティストを返します。$http リクエストで使用する URL を作成するために、callItunesは最初に makeUrl() を呼び出します。次に、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 オブジェクトのプロパティを設定します。上記の唯一のトリッキーなコードは、これまで promise を扱ったことがない場合です。callItunes は promise を返すため、.then() メソッドを使用して $scope.data.artistData を設定できるのは、iTunes データで promise が満たされた場合のみです。コントローラーが非常に「薄い」ことに気付くでしょう (これは適切なコーディング方法です)。すべてのロジックと永続データは、コントローラーではなく、サービス内にあります。
2) サービス
Service を作成する際に知っておくべき最大のことは、それが「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 コンストラクターがオブジェクトを返していることがわかります。これは、コードで確認できるかどうかにかかわらずです。第 2 に、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;
}
JavaScript で「new」キーワードが実際に何をするかについての知識があれば、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;
}
});
これで、ファクトリと同様に、setArtist、getArtist、および callItunes が、myService を渡すコントローラーで使用できるようになります。これが 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);
})
}
});
前に述べたように、'new' が何をするのかを本当に理解すると、サービスは AngularJS のファクトリとほとんど同じになります。
3) プロバイダー
プロバイダーについて覚えておくべき最大のことは、アプリケーションの app.config 部分に渡すことができる唯一のサービスであるということです。これは、サービス オブジェクトの一部を変更してからアプリケーションの他の場所で使用できるようにする必要がある場合に非常に重要です。サービス/ファクトリに非常に似ていますが、いくつかの違いについて説明します。
まず、Service と Factory で行ったのと同様の方法で Provider を設定します。以下の変数は、「プライベート」およびヘルパー関数です。
app.provider('myProvider', function(){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
//Going to set this property on the config function below.
this.thingFromConfig = ‘’;
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
}
*再度、上記のコードの一部がわかりにくい場合は、詳細について説明している Factory セクションを参照してください。
プロバイダーは 3 つのセクションを持つと考えることができます。最初のセクションは、後で変更/設定される「プライベート」変数/関数です (上記を参照)。2 番目のセクションは、app.config 関数で使用できる変数/関数です。したがって、他の場所で使用できるようになる前に変更できます (上記も参照)。これらの変数は「this」キーワードにアタッチする必要があることに注意してください。この例では、app.config で変更できるのは「thingFromConfig」のみです。3 番目のセクション (以下に示す) は、特定のコントローラーに「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
}
}
});
ファクトリとサービスと同様に、setArtist、getArtist、および callItunes は、myProvider を渡すコントローラーで使用できます。これが myProvider コントローラーです (これは、ファクトリ/サービス コントローラーとほぼ同じです)。
app.controller('myProviderCtrl', function($scope, myProvider){
$scope.data = {};
$scope.updateArtist = function(){
myProvider.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myProvider.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
前述のように、Provider を使用してサービスを作成することの要点は、最終的なオブジェクトがアプリケーションの残りの部分に渡される前に、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 に表示されると、「この文が設定されました…」となります。
私にとって、啓示は、それらがすべて同じように機能することに気付いたときに来ました。一度何かを実行し、取得した値を保存し、依存性注入によって参照されたときに同じ保存された値を吐き出します。
私たちが持っているとしましょう:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
3つの違いは次のとおりです。
a
の保存された値は、実行中のものfn
です。b
の格納された値はnew
ingから取得されますfn
。c
の格納された値は、最初にnew
ingによってインスタンスを取得し、次にインスタンスのメソッドをfn
実行することによって得られます。$get
つまり、AngularJS 内にキャッシュ オブジェクトのようなものがあり、各注入の値は、最初に注入されたときに 1 回だけ割り当てられます。
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
これが、in サービスを使用し、 in プロバイダーthis
を定義する理由です。this.$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 メソッドと constant メソッドのセクションにあります。
<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>
私にとって、違いを理解するための最良かつ最も簡単な方法は次のとおりです。
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
}
}
最も単純な考え方は次のとおりです。
- Service はシングルトン オブジェクト インスタンスです。コードにシングルトン オブジェクトを提供する場合は、サービスを使用します。
- 工場はクラスです。コードにカスタム クラスを提供する場合は、ファクトリを使用します (サービスは既にインスタンス化されているため、サービスでは実行できません)。
ファクトリの「クラス」の例は、プロバイダーの違いと同様に、コメントで提供されています。
Angular docsからの要約:
- オブジェクトの作成方法を定義するレシピ タイプには、 Value、 Factory、Service、Provider、およびConstantの 5 つがあります。
- FactoryとServiceは、最も一般的に使用されるレシピです。それらの唯一の違いは、Serviceレシピがカスタム タイプのオブジェクトに対してより適切に機能するのに対し、Factoryは JavaScript プリミティブと関数を生成できることです。
- Providerレシピはコア レシピ タイプであり、他のすべてのレシピは単なるシンタックス シュガーです。
- Providerは、最も複雑なレシピ タイプです。グローバル構成を必要とする再利用可能なコードを作成する場合を除き、これは必要ありません。
SOからのベストアンサー:
https://stackoverflow.com/a/26924234/165673 (<-- 良い)
https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673
すでにすべての良い答え。ServiceとFactoryについてさらにいくつかの点を追加したいと思います。サービス/工場の違いとともに。また、次のような質問をすることもできます。
- サービスまたは工場を使用する必要がありますか? 違いは何ですか?
- 彼らは同じことをしていますか、それとも同じ振る舞いをしていますか?
Service と factory の違いから始めましょう。
両方ともシングルトンです: Angular がこれらを初めて依存関係として見つけると、サービス/ファクトリの単一のインスタンスが作成されます。インスタンスが作成されると、同じインスタンスが永久に使用されます。
動作を持つオブジェクトをモデル化するために使用できます: どちらもメソッド、内部状態変数などを持つことができます。ただし、そのコードの記述方法は異なります。
サービス:
サービスはコンストラクター関数であり、Angular は new を呼び出してインスタンス化しますyourServiceName()
。これはいくつかのことを意味します。
- 関数とインスタンス変数は のプロパティになります
this
。 - 値を返す必要はありません。Angular が ) を呼び出すと、設定したすべてのプロパティを含むオブジェクトを
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
てスコアを初期化した場合など、プロミス チェーンを呼び出したくなるかもしれません。より良い方法は. これをサービス メソッドで使用するかどうかに関係なく、呼び出し方に注意してください。$http.get('/score').then(ScoreKeeper.setScore).
ScoreKeeper.setScore
this
null
$http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))
から値を返すService
:
(i.e., an Object)
JavaScript コンストラクターの仕組みにより、関数から複雑な値を返す場合constructor
、呼び出し元は this インスタンスではなくそのオブジェクトを取得します。
これは、基本的に、工場出荷時の例を以下からコピーして貼り付け、 に置き換えるfactory
と機能することを意味しますservice
。
angular.service('MyService', function($http) {
var api = {};
api.aServiceMethod= function() {
return $http.get('/users');
};
return api;
});
そのため、Angular が new MyService() を使用してサービスを構築すると、MyService インスタンスの代わりにその api オブジェクトが取得されます。
これは、複雑な値 (オブジェクト、関数) の動作ですが、プリミティブ型の動作ではありません。
工場:
ファクトリは、値を返す単純な古い関数です。戻り値は、ファクトリに依存するものに注入されるものです。Angular の典型的なファクトリ パターンは、次のように関数をプロパティとして持つオブジェクトを返すことです。
angular.factory('MyFactory', function($http) {
var api = {};
api.aFactoryMethod= function() {
return $http.get('/users');
};
return api;
});
ファクトリの依存関係に注入された値はファクトリの戻り値であり、オブジェクトである必要はありません。それは関数かもしれません
上記の 1 と 2 の質問に対する回答:
ほとんどの場合、すべてにファクトリを使用することに固執してください。彼らの行動は理解しやすいです。値を返すかどうかを選択することはできません。さらに、間違ったことをしてもバグが発生することはありません。
ただし、依存関係として注入することについて話しているときは、それらを「サービス」と呼んでいます。
Service/Factory の動作は非常に似ており、どちらでも問題ないと言う人もいます。それはある程度正しいですが、John Papa のスタイル ガイドのアドバイスに従い、ファクトリに固執する方が簡単だと思います。**
これは、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>
より簡単な例を次に示します。緯度と経度を公開する「位置」オブジェクトを期待するサードパーティのライブラリをいくつか使用していますが、異なるオブジェクト プロパティを使用しています。ベンダー コードをハッキングしたくなかったので、渡していた "Position" オブジェクトを調整しました。
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
;
この回答は、トピック/質問に対処します
Factory、Service、および Constant は、プロバイダー レシピの上にある構文糖衣に過ぎませんか?
また
factory 、 service 、および providers が内部的にどのように類似しているか
基本的に何が起こるかは
プロバイダーの 2 番目の引数で指定したfactory()
it セットを作成して it( )を返すと、得られるのはそれだけですが、それ以外のプロパティ/メソッドはありません(つまり、これを構成することはできません)。function
$get
provider(name, {$get:factoryFn })
provider
$get
provider
工場のソースコード
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
};
it を返すときservice()
は、factory() に(サービスで提供したコンストラクターのインスタンスを返す)function
を注入し、それを返します。constructor
サービスのソースコード
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
};
したがって、基本的にどちらの場合も、提供した関数に設定されたプロバイダー $get を最終的に取得しますが、config ブロックの provider() で最初に提供できるように、$get 以外のものを提供できます。
私は多くの優れた答えを知っていますが、 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でビジネス ロジックを処理する 3 つの方法について説明しましょう。
サービス:
構文:
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>
サービスの特徴:
- 遅延インスタンス化: 注入されない場合、インスタンス化されません。したがって、それを使用するには、モジュールに注入する必要があります。
- Singleton : 複数のモジュールに注入された場合、すべてが 1 つの特定のインスタンスにのみアクセスできます。そのため、異なるコントローラー間でデータを共有すると非常に便利です。
工場
まず、構文を見てみましょう。
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;
}
コントローラーで上記の2つを使用します。
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 でサービスを作成する最も柔軟な方法です。
- 動的に構成可能なファクトリを作成できるだけでなく、ファクトリを使用するときにプロバイダ メソッドを使用して、アプリケーション全体のブートストラップ時に一度だけファクトリをカスタム構成できます。
- このファクトリは、カスタム設定を使用してアプリケーション全体で使用できます。つまり、アプリケーションが起動する前にこのファクトリを構成できます。
.service
実際、Angular のドキュメントでは、プロバイダー メソッドは、またはメソッドを使用してサービスを構成するときに実際に舞台裏で実行されるものであると述べられています.factory
。 - これ
$get
は、プロバイダー インスタンスに直接関連付けられる関数です。その関数はファクトリ関数です。つまり、メソッドに提供するために使用するものと同じです。.factory
その関数では、独自のサービスを作成します。この$get
プロパティは関数であり、プロバイダーをプロバイダーにするものです。AngularJS は、値が Angular がファクトリ関数として扱う関数である $get プロパティを持つことをプロバイダーに期待します。しかし、このプロバイダー設定全体を非常に特別なものにしているのはconfig
、サービスプロバイダー内に何らかのオブジェクトを提供できるという事実であり、通常、アプリケーション全体を構成できるステップで後で上書きできるデフォルトが付属しています。
Factory:ファクトリ内で実際にオブジェクトを作成し、それを返すファクトリ。
service: this キーワードを使用して関数を定義する標準関数があるサービス。
プロバイダー:プロバイダーには、定義した $get があり、データを返すオブジェクトを取得するために使用できます。
基本的に、プロバイダー、ファクトリー、およびサービスはすべてサービスです。Factory は、必要なのが $get() 関数だけである Service の特殊なケースであり、より少ないコードで記述できます。
サービス、ファクトリー、およびプロバイダーの主な違いは、その複雑さです。サービスは最も単純な形式で、ファクトリーはもう少し堅牢で、プロバイダーは実行時に構成可能です。
それぞれをいつ使用するかの概要は次のとおりです。
Factory : 提供する値は、他のデータに基づいて計算する必要があります。
Service : メソッドを含むオブジェクトを返しています。
Provider : 構成フェーズ中に、作成される前に作成されるオブジェクトを構成できるようにする必要があります。アプリが完全に初期化される前に、主にアプリ構成でプロバイダーを使用します。
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;
});
- 一方、service() メソッドを使用すると、コンストラクター関数を定義してサービスを作成できます。生の JavaScript オブジェクトの代わりに、プロトタイプ オブジェクトを使用してサービスを定義できます。factory() メソッドと同様に、関数定義でインジェクタブルも設定します。
- サービスを作成する最低レベルの方法は、provide() メソッドを使用することです。これは、.config() 関数を使用して構成できるサービスを作成する唯一の方法です。以前の to メソッドとは異なり、定義済みの this.$get() 関数定義でインジェクタブルを設定します。
シンタクティック シュガーが違います。プロバイダーのみが必要です。つまり、プロバイダーのみが実際の角度であり、他のすべてのものは派生しています (コードを削減するため)。値だけを返し、計算や関数を返さない Value() と呼ばれる単純なバージョンもあります。Value もプロバイダーから派生します。
では、なぜこのような複雑な問題が発生するのでしょうか。なぜ provider だけを使用して、他のすべてを忘れることができないのでしょうか? コードを簡単に記述し、コミュニケーションを改善するのに役立つはずです。そして、冗談めかして答えると、フレームワークが複雑になればなるほど、フレームワークの売り上げが向上します。
- 値を返すことができるプロバイダ = 値
- インスタンス化して返すだけのプロバイダー = Factory (+ Value)
- インスタンス化できるプロバイダー + 何かを実行 = Service (+ Factory, + Value)
- プロバイダー = $get というプロパティが含まれている必要があります (+Factory、+ Service、+ Value)
角度注入は、この結論に到達するための最初のヒントを与えてくれます。
「$injector は、プロバイダによって定義されたオブジェクト インスタンスを取得するために使用されます」サービスではなく、ファクトリではなくプロバイダです。
「Angular サービスはサービス ファクトリによって作成されます。これらのサービス ファクトリは関数であり、サービス プロバイダによって作成されます。サービス プロバイダはコンストラクタ関数です。インスタンス化されると、プロパティが含まれている必要があります。 $get と呼ばれ、サービス ファクトリ関数を保持します。"
したがって、マスタープロバイダーとインジェクター、およびすべてが適切に機能します:)。また、Typescript では、IServiceProvider から継承することで $get をプロバイダーに実装できると興味深いものになります。