Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

It can't work normally within lazyload(loadChildren) NgModules #232

Closed
doxiaodong opened this issue Sep 8, 2016 · 29 comments
Closed

It can't work normally within lazyload(loadChildren) NgModules #232

doxiaodong opened this issue Sep 8, 2016 · 29 comments

Comments

@doxiaodong
Copy link

I'm submitting a ... (check one with "x")

[ ] bug report => check the FAQ and search github for a similar issue or PR before submitting
[x] support request => check the FAQ and search github for a similar issue before submitting
[ ] feature request

Current behavior
when I use lazyload(loadChildren) route, it's not easy to subscribe TranslateService change between each NgModules. I create a global translate service to subscribe the change but it fetch '*.json' when each NgModule loaded.

Expected/desired behavior
It can auto change the language within NgModules

Please tell us about your environment:

  • ng2-translate version: 2.4.3
  • Angular version: 2.0.0-rc.6
  • Browser: [all]
  • Language: [TypeScript 2.0-rc]
@doxiaodong doxiaodong changed the title It can't word normally within lazyload(loadChildren) NgModules It can't work normally within lazyload(loadChildren) NgModules Sep 8, 2016
@ocombe
Copy link
Collaborator

ocombe commented Sep 8, 2016

Did you define import the TranslateModule in your root app module ?

@doxiaodong
Copy link
Author

doxiaodong commented Sep 8, 2016

@ocombe, no, I only import the TranslateModule in the share module.

I have 4 modules, others are article, user, and root app. How could I do?

@ocombe
Copy link
Collaborator

ocombe commented Sep 8, 2016

if you import it in the shared module, you should add it to the exports of the shared module as well

@doxiaodong
Copy link
Author

@ocombe
Copy link
Collaborator

ocombe commented Sep 8, 2016

oh you're calling the service from within the module class, and you're calling "use" again there, you shouldn't have to do that

@doxiaodong
Copy link
Author

@ocombe, I forgot to tell you that I call 'use' in the root app https://github.com/doxiaodong/darlin-angular2/blob/develop/src/app/footer/footer.component.ts#L45, and if I do not call in a lazyload module(like article), it will look like as follow. image

@ocombe
Copy link
Collaborator

ocombe commented Sep 8, 2016

You should define all of that in the app root component: https://github.com/doxiaodong/darlin-angular2/blob/develop/src/app/app.component.ts
And that's all, not in any of the other sub modules

@doxiaodong
Copy link
Author

I'm sorry, I do it, but it doesn't work. doxiaodong/darlin-angular2@0ebc0ee

@dhardtke
Copy link
Contributor

dhardtke commented Sep 8, 2016

#217 is probably related to this. I have a similar setup and it's not working in lazy-loaded sub modules.

@JonnyBGod
Copy link

Also getting same behaviour/error.

Using https://github.com/AngularClass/angular2-webpack-starter and following your sharedModule setup.

Works fine for regular components but not for lazy loaded ones.

@krokerke
Copy link

I had the same problem.
using a module for setting "translate.use(LANG)" and import into every lazyload module fixed it for me.

@ocombe
Copy link
Collaborator

ocombe commented Sep 22, 2016

Ok I just checked the problem, this is what I did:

  • import TranslateModule into my SharedModule
  • prepare the translation in my SharedModule:
@NgModule({
    imports: [
        ...MODULES,
        TranslateModule.forRoot({
            provide: TranslateLoader,
            useFactory: (http: Http) => new TranslateLocalStorageLoader(http, 'assets/i18n', '.json?t=' + (new Date().getTime())),
            deps: [Http]
        })
    ],
    declarations: [
        ...COMPONENTS,
        ...DIRECTIVES,
        ...PIPES
    ],
    providers: [
        ...PROVIDED_SERVICES
    ],
    exports: [
        ...COMPONENTS,
        ...DIRECTIVES,
        ...PIPES,

        // modules
        ...MODULES,
        TranslateModule
    ]
})
export class SharedModule {
    constructor(translate: TranslateService) {
        // get the current UserLang
        userLang = translate.getBrowserLang();

        // this language will be used as a fallback when a translation isn't found in the current language
        translate.setDefaultLang('en');

        // the lang to use, if the lang isn't available, it will use the current loader to get them
        translate.use(userLang);
    }
}

And voilà ! It works in all my lazy loaded modules, as long as they import the SharedModule

@dhardtke
Copy link
Contributor

dhardtke commented Sep 22, 2016

Well, first of all, thanks for having a look at this, but I noticed this does not completely fix the problem.

What it does fix is that no strings will be translated at all. What it doesn't fix is changing the language (using translate.use(...)) by calling that method in a non-lazy-loaded module won't translate / update any strings in the lazy-loaded module.

EDIT:
it works, if you only have one TranslateService used by making the translate property in your SharedModule static. But that is no good option (considering unit testing for example, and it leads to tight coupling).

export class SharedModule {
    public static translate;
    constructor(translate: TranslateService) {
        SharedModule.translate = translate;
        // ...code from above
    }
}
export class NavbarComponent {
    private switchLang(lang: string): void {
        // this.translate.use(lang);
        SharedModule.translate.use(lang);
    }
}

Could you please have a look at this again? Thank you.

@doxiaodong
Copy link
Author

It fetch '*.json' when each NgModule loaded

@ocombe
Copy link
Collaborator

ocombe commented Sep 23, 2016

ok, I still have a trick in my sleeve if I cannot find a way to fix this, I can make the translations and currentLang static so that you can have multiple instances but they keep the same "state", but it means that it won't be possible to have different instances of the service in different states (I don't think it's a good idea, but there may be some use cases).

I will try to fix it this week end, but I don't know if I'll have the time (because I need to prepare myself for angular connect next week).

@chpasha
Copy link

chpasha commented Oct 4, 2016

Singleton services provided in shared modules are still cloned by all lazy modules which is an expected behaviour, look here https://angular.io/docs/ts/latest/guide/ngmodule.html#!#shared-module

Do not specify app-wide singleton providers in a shared module. A lazy loaded module that imports that shared module will make its own copy of the service

@ocombe
Copy link
Collaborator

ocombe commented Oct 4, 2016

Ok, static instances it is then, thanks for the info

@chpasha
Copy link

chpasha commented Oct 4, 2016

Ok, static instances it is then, thanks for the info

I'm not sure about that. I think the correct behaviour was already proposed here #209
We just have to configure TranslationModule with forRoot call in AppModule and then import and re-export it in SharedModule. This solution seems to work for me, strings are translated in both main and lazy modules and resource bundles are not loaded more than once

@SamVerschueren
Copy link
Collaborator

SamVerschueren commented Oct 6, 2016

@chpasha Yes, that is the correct way to do this.

Never ever call .forRoot() of a module in a shared module. It is called forRoot for a reason.

@dhardtke
Copy link
Contributor

dhardtke commented Oct 6, 2016

Yep, SamVerschueren's proposed solution works perfectly for me.

@doxiaodong
Copy link
Author

@SamVerschueren nice, and should I close this issue? @ocombe

@ocombe ocombe closed this as completed Oct 7, 2016
@ocombe
Copy link
Collaborator

ocombe commented Oct 7, 2016

Yes :)
If someone wants to update the docs to explain this better that'd be nice

@neolanders
Copy link

neolanders commented Feb 21, 2017

Since I didn't found any plunker working with ngx-translate librairy and I also had some difficulties to manage to make it work with LoadChildren,
I've setup a way that work pretty well for me:

I've created two SharedModules, (one for lazyLoading and one for the other part of my application)

SharedLazyModule for lazy loading content:

@NgModule({
  imports: [
    HttpModule,
    CommonModule,
    TranslateModule.**forChild**({}),
  ],
  exports: [
    CommonModule,
    TranslateModule
  ]
})
export class SharedLazyModule {}

SahredModule for App

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: Http) {
   return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
    imports: [
      HttpModule,
      CommonModule,
      TranslateModule.**forRoot**({
           provide: TranslateLoader,
           useFactory: HttpLoaderFactory,
           deps: [Http],
         })
    ],
    exports: [
      CommonModule,
      TranslateModule
    ]
})
export class SharedModule {
    
     constructor(private translate: TranslateService) {
                  
        translate.addLangs(["en", "fr"]);
        translate.setDefaultLang('en');

        let browserLang = translate.getBrowserLang();
        translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
    }
}

See Plunker:
https://plnkr.co/LVmIuI1Xw9vFn0IuC2jW

@dancancro
Copy link

Hi @neolanders What about putting the second one into a Core module along with all the singletons, per the style guide. Or is this a different animal? I'm trying to get this working in this app that contains many lazy loaded features. Currently only the dashboard needs i18n support.

@neolanders
Copy link

neolanders commented Jun 28, 2017

Hi @dancancro, you are totaly right. The SharedModule could be the appModule or any other CoreModule.
The only consern should be to use the "forRoot" for your modules imports and in your LazyShared Modules should use the "forChild" as per the style guide.

@dancancro
Copy link

dancancro commented Jun 29, 2017

@neolanders Thanks. Can you take a look at my project and see what I might be doing wrong? It is based on JHipster and so the language services are provided by way of JhiLanguageService in the ng-jhipster library which uses a JhiConfigService to configure ngx-translate without my needing to import and configure the TranslateModule in my app.module. So when I add TranslateModule.forRoot(...) to imports of AppModule, everything breaks and I just see "translation not found..." messages everywhere.

I don't know how to make my lazy-loaded pages use the same translation instances as the eager-loaded part of the app so that changing the language in an eager component of the nav bar affects the language used by the lazy-loaded pages.

I have also tried this idea but I think it suffers from the same problem that I don't know how to control ngx-translate configuration in a JHipster app. This is probably more a JHipster question but maybe you have some ideas. I'm asking in different places. Such is life when using open source software.

@karthic200mw
Copy link

karthic200mw commented Jan 16, 2023

Hi @dancancro Did you fixed the above JHipster issues.
@ocombe @deepu105 could you please help me with this
Is it possible to detach this package and we can integrate into directly into our app.

On Following file, this library has its own loader, so in the browser two times the json files is getting loaded, one from app.modules.ts and this below json from node_modules folder.

[Visit] (https://github.com/jhipster/ng-jhipster/blob/v0.3.6/index.ts )

import { NgModule, ModuleWithProviders, Sanitizer } from '@angular/core';
import { HttpModule } from '@angular/http';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { TranslateModule, TranslateLoader, MissingTranslationHandler, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

import { JHI_PIPES, JHI_DIRECTIVES, JHI_COMPONENTS, JHI_SERVICES } from './src/jhi-components';
import {
    JhiMissingTranslationHandler,
    JhiTranslateComponent,
    JhiLanguageService
} from './src/language';
import { JhiModuleConfig } from './src/config';
import { JhiConfigService } from './src/config.service';
import { JhiAlertService } from './src/service';

// Re export the files
export * from './src/pipe';
export * from './src/directive';
export * from './src/service';
export * from './src/component';
export * from './src/language';
export * from './src/interceptor';

export function translatePartialLoader(http: HttpClient) {
    return new TranslateHttpLoader(http, 'i18n/', `.json?buildTimestamp=${process.env.BUILD_TIMESTAMP}`);
}

export function missingTranslationHandler(configService: JhiConfigService) {
    return new JhiMissingTranslationHandler(configService);
}

@NgModule({
    imports: [
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: translatePartialLoader,
                deps: [HttpClient]
            },
            missingTranslationHandler: {
                provide: MissingTranslationHandler,
                useFactory: missingTranslationHandler,
                deps: [JhiConfigService]
            }
        }),
        HttpClientModule,
        HttpModule,
        CommonModule
    ],
    declarations: [
        ...JHI_PIPES,
        ...JHI_DIRECTIVES,
        ...JHI_COMPONENTS,
        JhiTranslateComponent
    ],
    exports: [
        ...JHI_PIPES,
        ...JHI_DIRECTIVES,
        ...JHI_COMPONENTS,
        JhiTranslateComponent,
        TranslateModule,
        HttpClientModule,
        HttpModule,
        CommonModule
    ]
})
export class NgJhipsterModule {
    static forRoot(moduleConfig: JhiModuleConfig): ModuleWithProviders {
        return {
            ngModule: NgJhipsterModule,
            providers: [
                ...JHI_SERVICES,
                { provide: JhiLanguageService, useClass: JhiLanguageService, deps: [TranslateService, JhiConfigService] },
                { provide: JhiAlertService, useClass: JhiAlertService, deps: [Sanitizer, JhiConfigService, TranslateService] },
                { provide: JhiModuleConfig, useValue: moduleConfig },
                { provide: JhiConfigService, useClass: JhiConfigService, deps: [JhiModuleConfig] }
            ]
        };
    }
}

@dancancro
Copy link

@karthic200mw Hi. Sorry, I have not looked at this project in years. Good luck.

@karthic200mw
Copy link

Thanks, There was an issue in the loading of webpack, For translation i am using the package @larscom/ngx-translate-module-loader. but added translation in main.json file and now its working fine. Thanks @deepu105 for adding support for the ngx-translate in the ngjhipster package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants