Search code examples
angularmicro-frontendangular14webpack-module-federationangular-module-federation

How to consume federated code directly in a component (Angular Module Federation)


I have a federated micro frontend remote component consumed in my main application, that works when it's consumed in the application routes:

path: 'panel',
 loadChildren: () => loadRemoteModule({
     type: 'module',
     remoteEntry: 'http://localhost:4201/remoteEntry.js',
     exposedModule: './Module',
 }).then(m => m.MainPanelModule)

Here is the MFE/remote webpack config:

module.exports = withModuleFederationPlugin({
  name: 'mainPanel',
  exposes: {
    './Module': './src/main-panel/main-panel.module.ts',
  },

  shared: {
    ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto', eager: true }),
  },
});

And here is the main application webpack config:

new ModuleFederationPlugin({
        remotes: {
          mainPanel: 'http://localhost:4201/remoteEntry.js'
        },
        shared: share({
          "@angular/core": { requiredVersion: 'auto', eager: true },
          "@angular/common": { requiredVersion: 'auto', eager: true },
          "@angular/common/http": { requiredVersion: 'auto', eager: true },
          "@angular/router": { requiredVersion: 'auto', eager: true },
        })

    }),

These configs work just fine - when I go to http://localhost:4200/panel I can see the remote mfe component. However, I need to consume it directly from another component in the application, not from the routes. For example, in the html, like so:

<main-panel></main-panel>

How can I do that? I am on Angular 14, just upgraded, and I can't go to the next version yet.

I have read various articles on the topic of Micro Frontend Implementation with Module Federation in Angular, but almost all of them are providing an example of consuming the remote from the routes. I found one article where they discuss a potential solution, but they are using another library, and I need to do it without adding any new libraries (if possible). Can this be done?


Solution

  • For anyone still trying to figure this out, I got the html using viewChild and then used the loadRemoteModule method in the code to get the remoteEntry.js file:

    ts file:

    @ViewChild('placeHolder', { read: ViewContainerRef }) viewContainer!: ViewContainerRef;
    
    ngOnInit() {
          this.loadRemote();
    }
    
    async loadRemote(): Promise<void> {
          const m = await loadRemoteModule({
                  type: 'module',
                  remoteEntry: 'http://localhost:4201/remoteEntry.js',
                  exposedModule: './Component'
              });
              const componentRef = this.viewContainer.createComponent(m.MainPanelComponent);
      }
    

    html file:

    <template #placeHolder></template>
    

    This works when you are running your MFE locally, but if you are publishing it to S3 (and it's not in a public bucket), you can create your own endpoint using Express router, and do the S3 logic in the background. Mine works using the getObject method from AWS SDK for S3 (without running the MFE locally). If your S3 bucket is public however, you can just replace the localhost URL with the S3 one, I believe.

    UPDATE:

    If you hit an "Automatic publicPath is not supported in this browser" browser console error, make sure to set target to "es2020" in your tsconfig compiler options.