Angularのservice, factory, providerの使い分け #scripty01
SCRIPTY#1 〜フロントエンド紳士・淑女のための勉強会〜 - connpass
「Angular.jsとThree.jsを一緒に使った時の話」というタイトルでLTしました。 資料とかは後日会社のブログにて公開されるのでもう少々お待ちください。
で、自分の発表の時に「service, factory, providerをどう使い分けているか」という質問があって、なんかうまく回答できたか自信がなかったのと、ちょっと間違ったことを言ってしまったので、ここで補足したいと思います。
単純な違い
Service
.service()
に渡した関数がコンストラクタとして実行され、DIされる
angular.module('App') // exampleをserviceで定義 .service('example', function () { this.methodA = function () { console.log('call methodA!!'); } }) // exampleをInjectする .controller('Ctrl', ['example', function (example) { example.methodA(); // 'call methodA!!'とコンソールに出力される }]);
Factory
.factory()
に渡した関数を実行した返り値がDIされる
angular.module('App') // exampleをserviceで定義 .factory('example', function () { function Example() { ... } Example.prototype.methodA = function () { console.log('call methodA!!'); } // これがcontrollerや他のserviceにInjectされる return new Example() }) // exampleをInjectする .controller('Ctrl', ['example', function (example) { example.methodA(); // 'call methodA!!'とコンソールに出力される }]);
Provider
- factory, serviceと大きく違うのがconfigブロックに渡せること
- configブロックはアプリケーション開始前に実行される
- configブロックでproviderのプロパティの変更ができる
- providerの
$get
を実行した返り値がDIされる
angular.module('App') // exampleをproviderで定義 .provider('example', function () { // 外に晒す設定値 this.options.message = 'call method of example.'; // こいつの返り値がcontrollerや他のserviceにInjectされる this.$get = function () { var message = this.options.message; // これがInjectされる return { methodA: function () { console.log(message); } } } }) // configブロックで使う場合、 // サービス名 + ProviderでInjectする。 // 今回の場合、example + Provider = exampleProvider .config(['exampleProvider', function (exampleProvider) { // 設定値をいじり挙動を変えることができる exampleProvider.options.message = 'change behavior of methodA.'; }]) // exampleをInjectする .controller('Ctrl', ['example', function (example) { example.methodA(); // 'change behavior of methodA.'とコンソールに出力される }]);
使い分け
provider
- 外部に公開するライブラリなど、再利用性を高くしたい場合にproviderを使う
serviceとfactory
.service
と.factory
については「これだ!」っていう理由が無い限り、- シンプルな
.service
で定義すればいいんじゃないかと思う。
これだ!っていう理由
- Angularの外で定義しているクラスとかをそのままインスタンス化して使いたい
.factory('example', function () { // Angularの外で定義しているクラスをそのまま返す return new OutOfAngularWorldClass(); })
- valueに依存関係がほしい、または初期化関数が欲しい場合
// .valueは第2引数がそのままInjectされる .value('apiKey', 'XXXXXXX') // 依存関係がほしい&初期化したい .factory('apiKey', ['otherService', function (otherService) { // 例えば別のサービスの値や関数を使って初期化して返す return otherService.someValue + 'XXXXXXX'; }]);
注意点
provider, factory, serviceともにシングルトンになっていて、生成後は内部でキャッシュされる。 なので下記のようなfactoryは最初にInjectされたタイミングで生成された値で固定されてしまう
.factory('randomValue', function () { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript var guid = (function() { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return function() { return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); }; })(); return guid(); });
Angular2.0でどうなるか
今日のQ&Aの時に「2.0ではfactory, serviceはなくなるかも」と言いましたが、あれは自分の記憶違いでした。 ごめんなさい。。。
正確には、
.config
,.constant
,.provider
が廃止され.constant
は.value
に一本化、.provider
は下記のように.config
ブロックではなく.value
で設定値の変更が可能になる
module.value('$http.config', { defaultHeaders: { // ... } });
でした。
2.0でDI周りがどうなるかは以前ブログに書いたのと、ここにコードと仕様があるのでそちらを見てください。(ブログの方はちょっとふるくなっているかもです)
まとめ
- 今はAngular.jsの本が2冊も出てるのでそれを見てからやればいいと思う。
- 公式のドキュメントが充実しているのでそちらを出来る限り見るのと、
- derectiveに関しては
ng
モジュールのソースを見て勉強するといいと思う