AngularJSの組み込みのservice($location等)をAngularへDIする方法
最近、仕事でAngularJSからAngularへのアップグレードをしています。その辺りのことは、ペパボ EC テックカンファレンスでも同僚の@ku00_がトークするので、興味のある方はそちらもどうぞ。
今のプロジェクトでは、ngUpgrade を利用してAngularJSとAngularを共存させる形で、徐々に移行しています。
その際に詰まった所が、調べても中々hitしなかったので、ブログにしておきます。
問題
ngUpgradeを利用して、AngularJsとAngularのハイブリットのアプリケーションを動かす時、Angularコンポーネントの中でAngularJSの組み込みのservice達 ($location, $log等) を使いたいケースがありました。
具体的に自分たちに起きたケースとしては、ボトムアップ的にAngularへの置き換えをしている中で、AngularからAngularJSで定義しているrouteへ遷移したいというものです。AngularJSでは、 $location.path('/newRoute')
を呼び出せばいいだけですが、それをAngularでやるにはどうすればいいでしょうか?
解決方法
公式ドキュメントを読むと、AngularJS の依存性を Angular に注入できるようにする という セクションがあります。これをすると、AngularJSの世界からAngularの世界へDIが可能になるようです。
サンプルとして書かれていたものは、組み込みのserviceではなく、自作のserviceの方法でした。
import { HeroesService } from './heroes.service';
@NgModule({
providers: [
{
provide: HeroesService,
useFactory: ($injector: any) => $injector.get('heros'),
deps: ['$injector']
}
]
})
DIするときは、普通に型でDI出来ます。
constructor(heroesService: HeroesService) {}
これで providers
に指定しているのは、FactoryProvider と呼ばれるものです。provide
には、型やInjectionToken と呼ばれる文字列を指定することが出来て、どのような形でDIするかが、ここの指定によって変わります。classを指定した場合は、そのclassでinjection出来ます。ただし、interfaceを指定することは出来ないため、AngularJSの組み込みserviceを指定する場合は文字列がよさそうです。
useFactory
関数ではDIする実体を決定できて、$injector
を使うとAngularJSの世界でDI可能なものを取得できるので、それを利用しているようです。
$location
のようなAngularJSなどの組み込みのserviceをDIする例です。
@NgModule({
providers: [
{
provide: '$location',
useFactory: ($injector: any) => $injector.get('$location'),
deps: ['$injector']
}
]
})
DIするとき
constructor(@Inject('$location') $location) {}
このように、provide
で文字列を指定した場合は、DIするときに @Inject
を利用する必要があります。Angularの providers がやっていることやDIが、なんとなくわかってきたような気がします。