question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bug(scope): Transloco with NX and module federation

See original GitHub issue

Is there an existing issue for this?

  • I have searched the existing issues

Which Transloco package(s) are the source of the bug?

Transloco

Is this a regression?

No

Current behavior

In my NX monorepo I have 2 apps angular. I’m using module federation to have access to the 2nd app from the first one. Transloco is working fine in the first app but in the second app I have the following error : -ERROR Error: NG0200: Circular dependency in DI detected for TranslocoService

Expected behavior

Would like to be able to use transloco in every app of the nx monorepo

Please provide a link to a minimal reproduction of the bug

I don’t have link to provide

Transloco Config

No response

Please provide the environment you discovered this bug in

Transloco: 4.0.0
Angular: 13.3.2
Node: 16
Package Manager: 
OS: ubuntu

Browser

Chrome v101.0.4951.41

Additional context

No response

I would like to make a pull request for this bug

No

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:5

github_iconTop GitHub Comments

2reactions
Choupa-webcommented, May 10, 2022

First last version of NX is installed. the module federation settings is the following: I have a shell angular app and a host angular app. IN THE SHELL APP:

app.modules.ts

import { TranslocoRootModule } from './transloco/transloco-root.module';
@NgModule({
  declarations: [AppComponent, NotFoundComponent, ConfirmDialogComponent],
  schemas: [NO_ERRORS_SCHEMA],
  imports: [
    ApmModule,
    AppRoutingModule,
    KeycloakAngularModule,
    HttpClientModule,
    TranslocoModule,
    AppMaterialModule,
    BboxMenuModule,
    BboxHeaderModule,
    BboxProgressSpinnerBarModule,
    FlexModule,
    BboxErrorPageModule,
    AllRecommendationsModule,
    ToastrModule.forRoot(ToasterGlobalCfg),
    BrowserAnimationsModule,
    FontAwesomeModule,
    TranslocoRootModule,
    BboxFooterModule
  ],
  providers: [
    ApmService,
    {
      provide: ErrorHandler,
      useClass: ApmErrorHandler
    },
    {
      provide: KeycloakService,
      useValue: keycloakService
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: GraviteeAPIInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: BboxSpinnerLoaderInterceptor,
      multi: true
    },
    HttpCancelService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: ManageHttpInterceptor,
      multi: true
    },
    {
      provide: MatPaginatorIntl,
      useClass: MatPaginatorIntlCustomService
    },
    {
      provide: TRANSLOCO_MISSING_HANDLER,
      useClass: CustomHandler
    }
  ],
  entryComponents: [AppComponent]
})
export class AppModule implements DoBootstrap {
  constructor(service: ApmService) {
    // API is exposed through this apm instance
    const { apmConfig } = environment;
    apmConfig.distributedTracingOrigins = [environment.baseUrl];

    // API is exposed through this apm instance
    const apm = service.init(apmConfig);

    apm.setUserContext({
      username: 'foo',
      id: 'bar'
    });
  }

  ngDoBootstrap(appRef: ApplicationRef): void {
    const { keycloakConfig } = environment; // A commenter si on ne veut pas passerpar keycloak
    keycloakService
      .init({
        config: keycloakConfig,
        initOptions: {
          onLoad: 'login-required',
          checkLoginIframe: false
        },
        loadUserProfileAtStartUp: true,
        bearerExcludedUrls: []
      })
      .then(() => {
        console.log('[ngDoBootstrap] bootstrap app');

        appRef.bootstrap(AppComponent);
      })
      .catch(error => console.error('[ngDoBootstrap] init Keycloak failed', error));
    // appRef.bootstrap(AppComponent); // A décommenter si on utilise pas Keycloack
  }
}

transloco-root.module.ts

import { HttpClient } from '@angular/common/http';
import { Translation, TRANSLOCO_CONFIG, TRANSLOCO_LOADER, translocoConfig, TranslocoLoader, TranslocoModule } from '@ngneat/transloco';
import { Injectable, NgModule } from '@angular/core';
import { environment } from '../../environments/environment';
import { Observable } from 'rxjs';
import { TranslocoLocaleModule } from '@ngneat/transloco-locale';

@Injectable({ providedIn: 'root' })
export class TranslocoHttpLoader implements TranslocoLoader {
  constructor(private http: HttpClient) {}

  getTranslation(lang: string): Observable<Translation> {
    return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
  }
}

@NgModule({
  exports: [TranslocoModule],
  imports: [
    TranslocoLocaleModule.forRoot({
      langToLocaleMapping: {
        en: 'en-US',
        fr: 'fr-FR'
      }
    })
  ],
  providers: [
    {
      provide: TRANSLOCO_CONFIG,
      useValue: translocoConfig({
        availableLangs: ['fr', 'en'],
        defaultLang: 'fr',
        // Remove this option if your application doesn't support changing language in runtime.
        reRenderOnLangChange: true,
        prodMode: environment.production as unknown as boolean
      })
    },
    { provide: TRANSLOCO_LOADER, useClass: TranslocoHttpLoader }
  ]
})
export class TranslocoRootModule {}

Within shell app few modules and their components have been added, example of one module

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

import { RecommendationRoutingModule } from './recommendation-routing.module';
import { RecommendationComponent } from './recommendation.component';
import { TranslocoModule } from '@ngneat/transloco';
import { FlexModule } from '@angular/flex-layout';
import { RecommendationMaterialModule } from './recommendation-material-module';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { TabNavBarComponent } from './tab-nav-bar/tab-nav-bar.component';
import { WorkflowModule } from '../shared/workflow/workflow.module';

@NgModule({
  declarations: [RecommendationComponent, TabNavBarComponent],
  exports: [RecommendationComponent, TranslocoModule],
  imports: [CommonModule, RecommendationRoutingModule, TranslocoModule, FlexModule, RecommendationMaterialModule, WorkflowModule]
})
export class RecommendationModule {
  customMatIcons: Array<string> = ['paperCoupon', 'drive', 'Workflow', 'inStore'];

  constructor(private matIconRegistry: MatIconRegistry, private sanitizer: DomSanitizer) {
    // we registry all custom icons.
    this.customMatIcons.forEach(iconKey => {
      this.matIconRegistry.addSvgIcon(iconKey, this.sanitizer.bypassSecurityTrustResourceUrl(`assets/icons/${iconKey}.svg`));
    });
  }
}

in the template of the component I’m using transloco pipe for the translation.

IN THE HOST APP:

app.module.ts

import { TranslocoRootModule } from './transloco-root.module';

@NgModule({
  declarations: [AppComponent, RefclearingListComponent, EditCodeComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MatTableModule,
    MatPaginatorModule,
    MatSortModule,
    MatFormFieldModule,
    MatInputModule,
    MatSelectModule,
    ReactiveFormsModule,
    MatNativeDateModule,
    MatDatepickerModule,
    NgxHateoasClientModule.forRoot(),
    MatButtonModule,
    TranslocoRootModule,
    MatIconModule,
    MatProgressSpinnerModule,
    RouterModule.forRoot(
      [
        {
          path: '',
          loadChildren: () =>
            import('./remote-entry/entry.module').then(
              (m: { RemoteEntryModule: RemoteEntryModule }) => m.RemoteEntryModule
            )
        }
      ],
      { initialNavigation: 'enabledBlocking' }
    )
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(hateoasConfig: NgxHateoasClientConfigurationService) {
    hateoasConfig.configure({
      http: {
        rootUrl: `${environment.baseUrl}`
      },
      useTypes: {
        resources: [Code]
      }
    });
  }
}

entry.module.ts (for module federation)

import { TranslocoRootModule } from '../transloco-root.module';

const routes: Routes = [
  { path: 'all', component: RefclearingListComponent },
  { path: 'new', component: EditCodeComponent }
];

@NgModule({
  declarations: [RemoteEntryComponent],
  imports: [
    CommonModule,
    HttpClientModule,
    MatTableModule,
    MatPaginatorModule,
    MatSortModule,
    MatFormFieldModule,
    MatInputModule,
    MatSelectModule,
    ReactiveFormsModule,
    MatNativeDateModule,
    MatDatepickerModule,
    MatButtonModule,
    TranslocoRootModule,
    MatIconModule,
    MatProgressSpinnerModule,
    NgxHateoasClientModule.forRoot(),
    RouterModule.forChild(routes)
  ],
  providers: [],
  exports: [RemoteEntryComponent]
})
export class RemoteEntryModule {
  constructor(hateoasConfig: NgxHateoasClientConfigurationService) {
    hateoasConfig.configure({
      http: {
        rootUrl: `${environment.baseUrl}`
      },
      useTypes: {
        resources: [Code]
      }
    });
  }
}

transloco-root.module.ts (different from the one in the shell app)

import { HttpClient } from '@angular/common/http';
import { TRANSLOCO_LOADER, Translation, TranslocoLoader, TRANSLOCO_CONFIG, translocoConfig, TranslocoModule } from '@ngneat/transloco';
import { Injectable, NgModule } from '@angular/core';
import { environment } from '../environments/environment';

@Injectable({ providedIn: 'root' })
export class TranslocoHttpLoader implements TranslocoLoader {
  constructor(private http: HttpClient) {}

  getTranslation(lang: string) {
    return this.http.get<Translation>(`/assets/i18n/${lang}.json`);
  }
}

@NgModule({
  exports: [TranslocoModule],
  providers: [
    {
      provide: TRANSLOCO_CONFIG,
      useValue: translocoConfig({
        availableLangs: ['fr', 'en'],
        defaultLang: 'fr',
        // Remove this option if your application doesn't support changing language in runtime.
        reRenderOnLangChange: true,
        prodMode: environment.production as unknown as boolean
      })
    },
    { provide: TRANSLOCO_LOADER, useClass: TranslocoHttpLoader }
  ]
})
export class TranslocoRootModule {}

Hope that will help. Let me know if you need more.

0reactions
Choupa-webcommented, May 31, 2022

Thank you for your solution. It works very well !!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Module Federation · Issue #490 · ngneat/transloco - GitHub
Has the team thought of including some way to work with Module Federation? I have tried using scopes but have not been able...
Read more >
Pitfalls with Module Federation and Angular
In this article, I'm going to destroy my Module Federation example! However, you don't need to worry: It's for a very good reason....
Read more >
ngneat-transloco/lobby - Gitter
Hi Guys, I am trying to join the scoped translation files using ng generate @ngneat/transloco:join but I receive the following error. Any ideas...
Read more >
Module Federation in Nx and Angular generates an error ...
I have checked the regular template generated by angular, it uses the defer style to everything, so the other js file entries are...
Read more >
I18N for Angular microfrontends using Ngx-Locutus
Every translation has a scope; Every translation is pure Typescript; Translations are registered ... Quilt — A module federation library and web-component.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found