diff --git a/README.md b/README.md index 232200ab..2a734355 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,11 @@ If you're importing directly from `node_modules`, you should edit your systemjs #### 1. Import the `TranslateModule`: -Finally, you can use ngx-translate in your Angular project. It is recommended to import `TranslateModule.forRoot()` in the NgModule of your application. +Finally, you can use ngx-translate in your Angular project. You have to import `TranslateModule.forRoot()` in the root NgModule of your application. -The [`forRoot`](https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root) static method is a convention that provides and configures services at the same time. Make sure you only call this method at the root module of your application, most of the time called `AppModule`. This method allows you to configure the `TranslateModule` loader. By default it will use the `TranslateStaticLoader`, but you can provide another loader instead as a parameter of this method (see below [Write & use your own loader](#write--use-your-own-loader)). +The [`forRoot`](https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root) static method is a convention that provides and configures services at the same time. +Make sure you only call this method in the root module of your application, most of the time called `AppModule`. +This method allows you to configure the `TranslateModule` by specifying a loader, a parser and/or a missing translations handler. ```ts import {BrowserModule} from '@angular/platform-browser'; @@ -51,7 +53,8 @@ export class AppModule { } ##### SharedModule -If you use a [`SharedModule`](https://angular.io/docs/ts/latest/guide/ngmodule.html#!#shared-modules) that you import in multiple other feature modules, you can easily export the `TranslateModule` to make sure you don't have to import it in every module. +If you use a [`SharedModule`](https://angular.io/docs/ts/latest/guide/ngmodule.html#!#shared-modules) that you import in multiple other feature modules, +you can export the `TranslateModule` to make sure you don't have to import it in every module. ```ts @NgModule({ @@ -63,7 +66,29 @@ If you use a [`SharedModule`](https://angular.io/docs/ts/latest/guide/ngmodule.h export class SharedModule { } ``` -> Note: Never call a `forRoot` static method in the `SharedModule`. You will end up with multiple different instances of a service in your injector tree. +> Note: Never call a `forRoot` static method in the `SharedModule`. You might end up with different instances of the service in your injector tree. But you can use `forChild` if necessary. + +##### Lazy loaded modules + +When you lazy load a module, you should use the `forChild` static method to import the `TranslateModule`. + +Since lazy loaded modules use a different injector from the rest of your application, you can configure them separately with a different loader/parser/missing translations handler. +You can also isolate the service by using `isolate: true`. In which case the service is a completely isolated instance (for translations, current lang, events, ...). +Otherwise, by default, it will share its data with other instances of the service (but you can still use a different loader/parser/handler even if you don't isolate the service). + +```ts +@NgModule({ + imports: [ + TranslateModule.forChild({ + loader: {provide: TranslateLoader, useClass: CustomLoader}, + parser: {provide: TranslateParser, useClass: CustomParser}, + missingTranslationHandler: {provide: MissingTranslationHandler, useClass: CustomHandler}, + isolate: true + }) + ] +}) +export class LazyLoadedModule { } +``` ##### Configuration @@ -93,9 +118,11 @@ export function HttpLoaderFactory(http: Http) { BrowserModule, HttpModule, TranslateModule.forRoot({ - provide: TranslateLoader, - useFactory: HttpLoaderFactory, - deps: [Http] + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [Http] + } }) ], bootstrap: [AppComponent] @@ -109,7 +136,7 @@ If you want to configure a custom `TranslateLoader` while using [AoT compilation ```ts export function createTranslateLoader(http: Http) { - return new TranslateStaticLoader(http, './assets/i18n', '.json'); + return new TranslateHttpLoader(http, './assets/i18n', '.json'); } @NgModule({ @@ -117,9 +144,11 @@ export function createTranslateLoader(http: Http) { BrowserModule, HttpModule, TranslateModule.forRoot({ - provide: TranslateLoader, - useFactory: (createTranslateLoader), - deps: [Http] + loader: { + provide: TranslateLoader, + useFactory: (createTranslateLoader), + deps: [Http] + } }) ], bootstrap: [AppComponent] @@ -154,7 +183,7 @@ export class AppComponent { #### 3. Define the translations: -Once you've imported the `TranslateModule`, you can put your translations in a json file that will be imported with the `TranslateStaticLoader`. The following translations should be stored in `en.json`. +Once you've imported the `TranslateModule`, you can put your translations in a json file that will be imported with the `TranslateHttpLoader`. The following translations should be stored in `en.json`. ```json { @@ -303,8 +332,7 @@ Once you've defined your loader, you can provide it in your configuration by add imports: [ BrowserModule, TranslateModule.forRoot({ - provide: TranslateLoader, - useClass: CustomLoader + loader: {provide: TranslateLoader, useClass: CustomLoader} }) ], bootstrap: [AppComponent] @@ -330,16 +358,18 @@ export class MyMissingTranslationHandler implements MissingTranslationHandler { } ``` -Setup the Missing Translation Handler in your module by adding it to the `providers` list. +Setup the Missing Translation Handler in your module import by adding it to the `forRoot` (or `forChild`) configuration. ```ts @NgModule({ imports: [ BrowserModule, - TranslateModule.forRoot() + TranslateModule.forRoot({ + missingTranslationHandler: {provide: MissingTranslationHandler, useClass: MyMissingTranslationHandler} + }) ], providers: [ - { provide: MissingTranslationHandler, useClass: MyMissingTranslationHandler } + ], bootstrap: [AppComponent] }) @@ -359,10 +389,6 @@ If you need it for some reason, you can use the `TranslateParser` service. ## FAQ -#### I'm getting an error `No provider for Http!` - -Because of the TranslateStaticLoader you have to load the HttpModule from `@angular/http`, even if you don't use this Loader - #### I'm still using Angular with beta / RC / 2.0 to 2.2, but I cannot use ngx-translate because I get errors?! I'm sorry but ngx-translate only supports Angular from version 2.3.0 and above because of changes in the framework... diff --git a/index.ts b/index.ts index 5cbfb765..8b230bb5 100644 --- a/index.ts +++ b/index.ts @@ -7,7 +7,6 @@ import {TranslateDirective} from "./src/translate.directive"; import {TranslatePipe} from "./src/translate.pipe"; import {TranslateStore} from "./src/translate.store"; import {USE_STORE} from "./src/translate.service"; -import {isDefined} from "./src/util"; export * from "./src/translate.loader"; export * from "./src/translate.service"; @@ -20,7 +19,8 @@ export interface TranslateModuleConfig { loader?: Provider; parser?: Provider; missingTranslationHandler?: Provider; - useStore?: boolean; + // isolate the service instance, only works for lazy loaded modules or components with the "providers" property + isolate?: boolean; } @NgModule({ @@ -47,7 +47,7 @@ export class TranslateModule { config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser}, config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler}, TranslateStore, - {provide: USE_STORE, useValue: isDefined(config.useStore) ? config.useStore : true}, + {provide: USE_STORE, useValue: config.isolate}, TranslateService ] }; @@ -65,7 +65,7 @@ export class TranslateModule { config.loader || {provide: TranslateLoader, useClass: TranslateFakeLoader}, config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser}, config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler}, - {provide: USE_STORE, useValue: isDefined(config.useStore) ? config.useStore : true}, + {provide: USE_STORE, useValue: config.isolate}, TranslateService ] }; diff --git a/src/translate.service.ts b/src/translate.service.ts index a0aca1c2..2d7c2ceb 100644 --- a/src/translate.service.ts +++ b/src/translate.service.ts @@ -56,7 +56,7 @@ export class TranslateService { * @type {EventEmitter} */ get onTranslationChange(): EventEmitter { - return this.useStore ? this.store.onTranslationChange : this._onTranslationChange; + return this.isolate ? this._onTranslationChange : this.store.onTranslationChange; } /** @@ -67,7 +67,7 @@ export class TranslateService { * @type {EventEmitter} */ get onLangChange(): EventEmitter { - return this.useStore ? this.store.onLangChange : this._onLangChange; + return this.isolate ? this._onLangChange : this.store.onLangChange; } /** @@ -78,20 +78,21 @@ export class TranslateService { * @type {EventEmitter} */ get onDefaultLangChange() { - return this.useStore ? this.store.onDefaultLangChange : this._onDefaultLangChange; + return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange; } /** * The default lang to fallback when translations are missing on the current lang */ get defaultLang(): string { - return this.useStore ? this.store.defaultLang : this._defaultLang; + return this.isolate ? this._defaultLang : this.store.defaultLang; } + set defaultLang(defaultLang: string) { - if(this.useStore) { - this.store.defaultLang = defaultLang; - } else { + if(this.isolate) { this._defaultLang = defaultLang; + } else { + this.store.defaultLang = defaultLang; } } @@ -100,13 +101,14 @@ export class TranslateService { * @type {string} */ get currentLang(): string { - return this.useStore ? this.store.currentLang : this._currentLang; + return this.isolate ? this._currentLang : this.store.currentLang; } + set currentLang(currentLang: string) { - if(this.useStore) { - this.store.currentLang = currentLang; - } else { + if(this.isolate) { this._currentLang = currentLang; + } else { + this.store.currentLang = currentLang; } } @@ -115,13 +117,14 @@ export class TranslateService { * @type {Array} */ get langs(): string[] { - return this.useStore ? this.store.langs : this._langs; + return this.isolate ? this._langs : this.store.langs; } + set langs(langs: string[]) { - if(this.useStore) { - this.store.langs = langs; - } else { + if(this.isolate) { this._langs = langs; + } else { + this.store.langs = langs; } } @@ -130,14 +133,14 @@ export class TranslateService { * @type {{}} */ get translations(): any { - return this.useStore ? this.store.translations : this._translations; + return this.isolate ? this._translations : this.store.translations; } set translations(translations: any) { - if(this.useStore) { - this.store.translations = translations; - } else { + if(this.isolate) { this._currentLang = translations; + } else { + this.store.translations = translations; } } @@ -147,13 +150,13 @@ export class TranslateService { * @param currentLoader An instance of the loader currently used * @param parser An instance of the parser currently used * @param missingTranslationHandler A handler for missing translations. - * @param useStore whether this service should use the store or not + * @param isolate whether this service should use the store or not */ constructor(public store: TranslateStore, public currentLoader: TranslateLoader, public parser: TranslateParser, public missingTranslationHandler: MissingTranslationHandler, - @Inject(USE_STORE) private useStore: boolean = true) { + @Inject(USE_STORE) private isolate: boolean = false) { } /** diff --git a/tests/translate.store.spec.ts b/tests/translate.store.spec.ts index 5a533399..43c4f849 100644 --- a/tests/translate.store.spec.ts +++ b/tests/translate.store.spec.ts @@ -127,10 +127,10 @@ describe("module", () => { })) ); - it("should create 2 instances of the service when lazy loaded using forChild and useStore false", fakeAsync(inject( + it("should create 2 instances of the service when lazy loaded using forChild and isolate true", fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - let LoadedModule = getLazyLoadedModule(TranslateModule.forChild({useStore: false})); + let LoadedModule = getLazyLoadedModule(TranslateModule.forChild({isolate: true})); loader.stubbedModules = {expected: LoadedModule}; const fixture = createRoot(router, RootCmp), @@ -153,7 +153,7 @@ describe("module", () => { })) ); - it("should relay events when lazy loading & using forChild with useStore true", fakeAsync(inject( + it("should relay events when lazy loading & using forChild with isolate false", fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { let LoadedModule = getLazyLoadedModule(TranslateModule.forChild()); @@ -178,10 +178,10 @@ describe("module", () => { })) ); - it("should not relay events when lazy loading & using forChild with useStore false", fakeAsync(inject( + it("should not relay events when lazy loading & using forChild with isolate true", fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - let LoadedModule = getLazyLoadedModule(TranslateModule.forChild({useStore: false})); + let LoadedModule = getLazyLoadedModule(TranslateModule.forChild({isolate: true})); loader.stubbedModules = {expected: LoadedModule}; const fixture = createRoot(router, RootCmp),