Ngx-admin: Implementing ng2-translate module

Created on 4 Oct 2016  路  10Comments  路  Source: akveo/ngx-admin

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [X] question about the decisions made in the repository

In fact I have no idea whether this is a bug or not but I will try to explain it so you can tell me if I am doing something wrong.

I am trying to implement the ng2-translate module. I could do it in pages.module but I am unable to do it in the nga.module.

I created a custom module called Pipes that exports the TranslateModule and when I implement it in pages.module by referencing to pipes.module and adding it to the imports array, everything seems to be ok.
Then I tried to extend it to nga.module in the same way, importing the PipesModule from source file and adding it to import section but despite the fact that I have no error, the translations of every single page are not loaded.

The following is the code I am doing, can you help me?

pipes.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, provideForms } from '@angular/forms';

import { TranslateModule, TranslateStaticLoader, TranslateLoader } from 'ng2-translate/ng2-translate';
import { Http, HttpModule } from '@angular/http';


@NgModule({
    declarations: [
    ],
    imports: [
        TranslateModule.forRoot({
          provide: TranslateLoader,
          useFactory: (http: Http) => new TranslateStaticLoader(http, 'assets/langs', '.json'),
          deps: [Http]
        })
    ],
    exports: [
        TranslateModule
    ]
})

export class PipesModule { }




pages.module.ts

import { NgModule }      from '@angular/core';
import { CommonModule }  from '@angular/common';

import { routing }       from './pages.routing';
import { NgaModule } from '../theme/nga.module';
import { PipesModule } from '../pipes.module';

import { Pages } from './pages.component';

@NgModule({
    imports: [
        CommonModule, 
        NgaModule, 
        routing,
        PipesModule
    ],
    declarations: [Pages]
})

export class PagesModule { }




nga.module.ts

import { NgModule }      from '@angular/core';
import { CommonModule }  from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { PipesModule } from '../pipes.module';

import {
  BaThemeConfig
} from './theme.config';

import {
  BaThemeConfigProvider
} from './theme.configProvider';

import {
  BaAmChart,
  BaBackTop,
  BaCard,
  BaChartistChart,
  BaCheckbox,
  BaContentTop,
  BaFullCalendar,
  BaMenu,
  BaMsgCenter,
  BaMultiCheckbox,
  BaPageTop,
  BaPictureUploader,
  BaSidebar,
  BaInternationalization,
  BaStripedTable,
  BaPreview,
  BaDialog
} from './components';

import {
  BaScrollPosition,
  BaSlimScroll,
  BaThemeRun
} from './directives';

import {
  BaAppPicturePipe,
  BaKameleonPicturePipe,
  BaProfilePicturePipe
} from './pipes';

import {
  BaImageLoaderService,
  BaThemePreloader,
  BaThemeSpinner
} from './services';

import {
  EmailValidator,
  EqualPasswordsValidator
} from './validators';

const NGA_COMPONENTS = [
  BaAmChart,
  BaBackTop,
  BaCard,
  BaChartistChart,
  BaCheckbox,
  BaContentTop,
  BaFullCalendar,
  BaMenu,
  BaMsgCenter,
  BaMultiCheckbox,
  BaPageTop,
  BaPictureUploader,
  BaSidebar,
  BaInternationalization,
  BaStripedTable,
  BaPreview,
  BaDialog
];

const NGA_DIRECTIVES = [
  BaScrollPosition,
  BaSlimScroll,
  BaThemeRun
];

const NGA_PIPES = [
  BaAppPicturePipe,
  BaKameleonPicturePipe,
  BaProfilePicturePipe
];

const NGA_SERVICES = [
  BaImageLoaderService,
  BaThemePreloader,
  BaThemeSpinner
];

const NGA_VALIDATORS = [
  EmailValidator,
  EqualPasswordsValidator
];

@NgModule({
  declarations: [
    ...NGA_PIPES,
    ...NGA_DIRECTIVES,
    ...NGA_COMPONENTS
  ],
  imports: [
    CommonModule,
    RouterModule,
    FormsModule,
    ReactiveFormsModule,
    PipesModule
  ],
  providers: [
    BaThemeConfigProvider,
    BaThemeConfig,
    ...NGA_VALIDATORS,
    ...NGA_SERVICES
  ],
  exports: [
    ...NGA_PIPES,
    ...NGA_DIRECTIVES,
    ...NGA_COMPONENTS
  ]
})
export class NgaModule {
}

needs investigation

All 10 comments

+1

I got it working doing the next steps:

1.- First I created a _core_ module which is a shared module and I want it visible app wide (like your pipes module is meant to be), it contains not only translate pipes but other stuff too. Like this (notice specially the exports section):

...
import { TranslateModule } from 'ng2-translate/ng2-translate';
...
@NgModule({
  imports: [
      CommonModule,
      TranslateModule.forRoot()
  ],
  exports: [
      TranslateModule,
      ...
  ]
})
export class CoreModule {
    constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
        if (parentModule) {
            throw new Error('CoreModule is already loaded. Import it in the AppModule only');
        }
    }
}

2.- I imported that core module into the app module like this:

...
import { CoreModule } from './common/core.module';
...
@NgModule({
  imports: [ // import Angular's modules
    BrowserModule,
    HttpModule,
    RouterModule,
    FormsModule,
    ReactiveFormsModule,
    appRouting,
    HomeModule,
    PagesModule,
    CoreModule
]
...

3.- Now the translate module is app wide available, however, every time you need to access to the translate service you still have to import it in each component / service where you use it (you also could bootstrap it, but I wouldn't recommend it, you don't always need TranslateService).

If I am not missing anything, that's about it.
That should work and I believe having a common shared module where the translate module is included makes sense since you will need it app wide.

Hopefully this helps.

marcos, thank you very much for the response!

I could not make it work using the "| translate" pipe in the template.

1- I created the pipes.module as in my first post (adding the constructor you added).
2- I imported it into app.module. (I can access it in the .component but not in the .html file)

The error is that the 'translate' pipe is not recognized: "Template parse errors: The pipe 'translate' could not be found".

Regards!

Hi, @nicokiva
The most important part is the "exports" section. You have to export the module that holds the translation logic so that when you import that in the app module it will be application wide available.
Honestly the constructor is not a big deal, you can skip making it the way I did if you want, those lines are only to throw a more friendly error when the core module is included twice. It is a good thing to do to help yourself, but it doesn't change the behaviour in a normal application if everything is correctly configured.
So, if you are getting that error is because your component can't see the pipe and that means your translate module is not actually available in that component's context.
Can you write your app structure and highlight the component that is not able to see the translate module?
Also, can you write at least the related parts of app.module and others modules involved in your issue so we can maybe see something you may be missing?
Thanks

Marcos! yes, of course.

src/app/app.Module
import { NgModule, ApplicationRef, ReflectiveInjector } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { removeNgStyles, createNewHosts } from '@angularclass/hmr';

/*
 * Platform and Environment providers/directives/pipes
 */
import { ENV_PROVIDERS } from './environment';
import { routing } from './app.routing';

// App is our top level component
import { App } from './app.component';
import { AppState } from './app.service';
import { GlobalState } from './global.state';
import { NgaModule } from './theme/nga.module';
import { PagesModule } from './pages/pages.module';
import { PipesModule } from './pipes.module';

import { Configuration } from './app.configuration';


import { IService } from './services/interfaces/account.service';

import { AccountService } from './services/account.service';
import { AccountServiceMock } from './services/mocks/account.service';

import { ProfileService } from './services/profile.service';
import { ProfileServiceMock } from './services/mocks/profile.service';

// Application wide providers
const APP_PROVIDERS = [
  AppState,
  GlobalState
];

/*
  If mockServices then load mock services (has sense only for those that calls backend)
  else load original services.
*/
let SERVICES = [];
var injectorOptions = [];
if (Configuration.mockServices) {
  SERVICES.push({ provide: AccountService, useClass: AccountServiceMock });
  SERVICES.push({ provide: ProfileService, useClass: ProfileServiceMock });
} else {
  SERVICES.push({ provide: AccountService, useClass: AccountService });
  SERVICES.push({ provide: ProfileService, useClass: ProfileService });
}


/**
 * `AppModule` is the main entry point into Angular2's bootstraping process
 */
@NgModule({
  bootstrap: [App],
  declarations: [
    App
  ],
  imports: [ // import Angular's modules
    BrowserModule,
    HttpModule,
    RouterModule,
    FormsModule,
    ReactiveFormsModule,
    NgaModule,
    PagesModule,
    routing,
    PipesModule  
  ],
  providers: [ // expose our Services and Providers into Angular's dependency injection
    ENV_PROVIDERS,
    APP_PROVIDERS,
    SERVICES
  ]
})

export class AppModule {

  constructor(public appRef: ApplicationRef, public appState: AppState) { }

  hmrOnInit(store) {
    if (!store || !store.state) return;
    console.log('HMR store', store);
    this.appState._state = store.state;
    this.appRef.tick();
    delete store.state;
  }

  hmrOnDestroy(store) {
    const cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement);
    // recreate elements
    const state = this.appState._state;
    store.state = state;
    store.disposeOldHosts = createNewHosts(cmpLocation);
    // remove styles
    removeNgStyles();
  }

  hmrAfterDestroy(store) {
    // display new elements
    store.disposeOldHosts();
    delete store.disposeOldHosts;
  }
}


src/app/pipes.module

import { NgModule, Optional, SkipSelf } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, provideForms } from '@angular/forms';

import { TranslateModule, TranslateStaticLoader, TranslateLoader } from 'ng2-translate/ng2-translate';
import { Http, HttpModule } from '@angular/http';


@NgModule({
    declarations: [
    ],
    imports: [
        TranslateModule.forRoot({
          provide: TranslateLoader,
          useFactory: (http: Http) => new TranslateStaticLoader(http, 'assets/langs', '.json'),
          deps: [Http]
        })
    ],
    exports: [
        TranslateModule
    ]
})

export class PipesModule {

  constructor (
    @Optional() @SkipSelf() parentModule: PipesModule
  ) {

    if (parentModule) {
        throw new Error('CoreModule is already loaded. Import it in the AppModule only');
    }

  }

}




src/app/theme/nga.module

import { NgModule }      from '@angular/core';
import { CommonModule }  from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import {
  BaThemeConfig
} from './theme.config';

import {
  BaThemeConfigProvider
} from './theme.configProvider';

import {
  BaAmChart,
  BaBackTop,
  BaCard,
  BaChartistChart,
  BaCheckbox,
  BaContentTop,
  BaFullCalendar,
  BaMenu,
  BaMsgCenter,
  BaMultiCheckbox,
  BaPageTop,
  BaPictureUploader,
  BaSidebar,
  BaInternationalization,
  BaStripedTable,
  BaPreview,
  BaDialog
} from './components';

import {
  BaScrollPosition,
  BaSlimScroll,
  BaThemeRun
} from './directives';

import {
  BaAppPicturePipe,
  BaKameleonPicturePipe,
  BaProfilePicturePipe
} from './pipes';

import {
  BaImageLoaderService,
  BaThemePreloader,
  BaThemeSpinner,
  BaSimpleSpinner,
  BaHttpWrapperService
} from './services';

import {
  EmailValidator,
  EqualPasswordsValidator
} from './validators';

const NGA_COMPONENTS = [
  BaAmChart,
  BaBackTop,
  BaCard,
  BaChartistChart,
  BaCheckbox,
  BaContentTop,
  BaFullCalendar,
  BaMenu,
  BaMsgCenter,
  BaMultiCheckbox,
  BaPageTop,
  BaPictureUploader,
  BaSidebar,
  BaInternationalization,
  BaStripedTable,
  BaPreview,
  BaDialog
];

const NGA_DIRECTIVES = [
  BaScrollPosition,
  BaSlimScroll,
  BaThemeRun
];

const NGA_PIPES = [
  BaAppPicturePipe,
  BaKameleonPicturePipe,
  BaProfilePicturePipe
];

const NGA_SERVICES = [
  BaImageLoaderService,
  BaThemePreloader,
  BaThemeSpinner,
  BaSimpleSpinner,
  BaHttpWrapperService
];

const NGA_VALIDATORS = [
  EmailValidator,
  EqualPasswordsValidator
];

@NgModule({
  declarations: [
    ...NGA_PIPES,
    ...NGA_DIRECTIVES,
    ...NGA_COMPONENTS
  ],
  imports: [
    CommonModule,
    RouterModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [
    BaThemeConfigProvider,
    BaThemeConfig,
    ...NGA_VALIDATORS,
    ...NGA_SERVICES
  ],
  exports: [
    ...NGA_PIPES,
    ...NGA_DIRECTIVES,
    ...NGA_COMPONENTS
  ]
})
export class NgaModule {
}

Having this, every component into src/app/pages can see the translation pipe but no component in src/app/theme/components can see it (at least I tried with baMessageCenter and baBackTop). Better said the .component files are injected with the translate module but the template does not recognize the translate pipe.

Hi @nicokiva

Well, I believe you're missing importing "pipesModule" in the nga.module imports section.
I don't see anything else. When you have multiple modules, you have to include that "shared module" (pipesModule) in each module you make use of it.
I know that sounds like if you import a module inside app.module it should be inherited down the stream to other children, but Angular2 doesn't work that way in order to isolate as much as possible the modules in stand-alone pieces and avoid circular references or very-hard-to-follow ones... I had myself quite a hard time getting my head around that, but it makes sense after all.

Hopefully this helps.

Yeah! it worked. I love you!

I added the import to nga.module and it worked!

I thought that as it was app wide after including it in the app.module, it was going to work but after importing it in the nga.modules, it worked!

Well... I do not know what happened but there is another issue I have now.

If I import PipesModule in app.module to make it app wide and then I import it in pages.module, everything is OK. After that if I import it in the nga.module, every translation from the page is lost (it is shown the keys I use to get them)...

@nicokiva
Import TranslateService in your app.module and inject it on constructor to set the default language, doing so the application will load with the choose language, like that:

constructor(public appRef: ApplicationRef, public appState: AppState, translate: TranslateService) {
  translate.setDefaultLang('en');
 }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

mignam picture mignam  路  3Comments

argnist picture argnist  路  4Comments

PatrickHuetter picture PatrickHuetter  路  3Comments

burtonator picture burtonator  路  3Comments

nfdavenport picture nfdavenport  路  3Comments