angular-architects / module-federation-plugin

MIT License
735 stars 203 forks source link

Missing providers from Remote #649

Closed JuNe98 closed 1 month ago

JuNe98 commented 2 months ago

I am currently implementing a MFE setup and tried to import all routes from Remote. This is working already but my components in the Remote are depending on an InjectionToken only provided in the Angular ApplicationConfig of the Remote.

Is there a way to load them in the shell? Or how do I have to handle them?

I have a complete standalone setup, so there are no modules.

Thanks in advance

PS: For my understanding I have to put all providers from app.config.ts to my rendered "root" component in the root called "portal" in my case. Am I right?

=============================================================================== Update - Add app context:

@angular-architects/native-federation => 18.2.2 @angular/* => 18.2.4

I have a service which initializes my app config before bootstrapping my application (remote). After that I save this config in a static variable and and provide it in my app.config.ts as an injection token for use in my whole app. This works perfectly without the MFE setup. So ist there a way to do this in my remotes with MFE setup?

app-config.service.ts:

import { Injectable, InjectionToken } from '@angular/core';
import { AppConfig } from '../models/app-config.model';

export const APP_CONFIG = new InjectionToken<AppConfig>('AppConfig');

@Injectable({
  providedIn: 'root',
})
export class AppConfigService {
  static config: AppConfig | undefined;

  static async loadAppConfig(): Promise<void> {
    const jsonFile = '/mfe1/assets/configs/app.config.json';
    await fetch(jsonFile)
      .then(async (config) => (this.config = await config.json()))
      .catch((error) => {
        console.error(`Could not load configuration file '${jsonFile}'.`, error);
        return Promise.reject();
      });
  }
}

bootstrap.ts:

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { AppConfigService } from '@shared/services';

AppConfigService.loadAppConfig().then(() => {
  bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
});

app.config.ts:

import { ApplicationConfig, LOCALE_ID, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { environment } from '@environment';
import { MAT_ICON_DEFAULT_OPTIONS } from '@angular/material/icon';
import { APP_CONFIG, AppConfigService } from '@shared/services';
import { provideHttpClient } from '@angular/common/http';
import { APP_BASE_HREF, registerLocaleData } from '@angular/common';
import localeDeAt from '@angular/common/locales/de-AT';

registerLocaleData(localeDeAt, 'de-AT');

export const appConfig: ApplicationConfig = {
  providers: [
    { provide: APP_BASE_HREF, useValue: '/mfe1' },
    {
      provide: APP_CONFIG,
      useFactory: () => AppConfigService.config,
    },
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideAnimationsAsync(),
    provideHttpClient(),
    {
      provide: MAT_ICON_DEFAULT_OPTIONS,
      useValue: { fontSet: 'material-symbols-outlined' },
    },
    {
      provide: LOCALE_ID,
      useValue: 'de-AT',
    },
  ],
};
manfredsteyer commented 1 month ago

Hi,

you really have to register them in the shell too. This is because Angular itself thinks this is just lazy loading while in fact it's loading a different micro frontend.