I am in the process of upgrading an AngularJS (i.e. Angular 1.6) application to Angular (i.e. Angular 4.1.3). I chose to do the incremental upgrade so currently both AngularJS and Angular are bootstrapped and running. So far so good, but: One of the AngularJS services that should be rewritten to Angular relies on a well known service $translate
which is part of the 3rd party AngularJS (read Angular 1) module pascalprecht.translate. In other words, $translate
service is injected to MyService
which should become an Angular (read Angular 4) service.
MyService
(stripped down) looks like this:
import { Injectable } from '@angular/core';
@Injectable()
export class MyService {
// Here comes the injection (how to do it?).
constructor(private $translate: $translate) {
}
foo() {
// Use the service
this.$translate('HELLO_WORLD');
}
}
It is located within MyModule
:
import { NgModule } from '@angular/core';
import { MyService } from './my.service';
@NgModule({
providers: [
MyService
]
})
export class MyModule {
}
Now, how can I inject $translate
into MyService
when MyService
resides within an Angular module while $translate
is part of a 3rd party AngularJS module?
I know how to inject an AngularJS service into an Angular service if the AngularJS service is located within the same module (or at least the module is part of my own solution). It is explained in the official docs. Is there any way to handle a 3rd party service? Do I need to register that service within MyModule
's providers?
import { NgModule } from '@angular/core';
import { MyService } from './my.service';
// How this line should look line then?
import { $translate } from 'node_modules/angular-translate/...';
@NgModule({
providers: [
MyService,
$translate
]
})
export class MyModule {
}
Or am I trying to achieve impossible?
Well, after a few hours of struggling I've finally found the solution. Here it is:
First of all, follow the Angular's official guidelines and upgrade the provider of the 3rd party service - $translate
. Like this:
ajs-upgraded-providers.ts
import { InjectionToken } from '@angular/core';
import { downgradeInjectable } from '@angular/upgrade/static';
import 'angular-translate';
// Variable 'i' holds the standard AngularJS injector
export function $translateFactory(i: any) {
return i.get('$translate');
};
// There is no class representing the good old $translate service so we have
// a non-class dependency. Therefore we use an InjectionToken (Angular 4) or
// OpaqueToken (Angular 2).
export let $translate = new InjectionToken('$translate');
// Finally create the upgraded provider
export const $translateProvider = {
provide: $translate,
useFactory: $translateFactory,
deps: ['$injector']
};
One thing to notice, the $translate
service might be dependent on other old AngularJS services like (in my case) $translateStaticFilesLoader
or $translateMessageFormatInterpolation
. If this is also your case, be sure to extend the code above and make upgraded providers for those services as well (keep them in the same file).
import
statement worksThe angular-translate
is installed as a node module so the statement
import 'angular-translate';
works just fine if your tsconfig.json
is set up to use moduleResolution: "node"
.
Then, of course, you need to ensure that the import
statement will work even after the code is transpiled from TypeScript to ES5 (or whichever target you use) and picked up by a module loader (SystemJS in my case).
Notice that we imported the angular-translate
script without getting anything from it, just to cause side effects. Basically, the import ensures that the script containing the desired service $translate
is simply executed and registers $translate
service for the AngularJS $injector.
Now ensure that the new upgraded provider of $translate
service is registered among other providers of MyModule
.
my.module.ts
import { NgModule } from '@angular/core';
import { MyService } from './my.service';
import { $translateProvider } from './ajs-upgraded-providers';
@NgModule({
providers: [
MyService,
$translateProvider
]
})
export class MyModule {
}
Finally, use the $translate
service in MyService
.
my.service.ts
import { Injectable, Inject } from '@angular/core';
import { $translate } from './ajs-upgraded-providers';
@Injectable()
export class MyService {
// Notice the usage of InjectionToken
constructor(@Inject($translate) private $translate: any) {
}
foo() {
this.$translate('hello.world').then((translation: string) => {
console.log(translation);
});
}
}