angular-architects / module-federation-plugin

MIT License
735 stars 203 forks source link

React MFE inside an Angular Shell #547

Open sistla001 opened 6 months ago

sistla001 commented 6 months ago

I have created an Angular Shell and an Angular MFE according to https://www.angulararchitects.io/en/blog/micro-frontends-with-modern-angular-part-1-standalone-and-esbuild/ which works. Then I created a react remote app using https://dev.to/abhi0498/react-micro-frontends-using-vite-30ah#setting-up-the-remote-app

I am getting the following error in the console of the shell app when going to http://localhost:4200/react

image

Below is my setup

in vite.config.ts of the React MFE1

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from "@originjs/vite-plugin-federation";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'react-vite-mfe1',
      filename: 'remoteEntry.js',
      exposes: {
        './react-vite-mfe1': './src/App',
      },
      shared: ['react','react-dom']
    })
  ],
  build: {
    target: 'esnext'
  }
})

and in angular shell project, I did the following federation.manifest.json

{
    "mfe1": "http://localhost:4201/remoteEntry.json",
  "react-vite-mfe1": "http://localhost:4173/assets/remoteEntry.js"
}

app-wrapper component:

import { CommonModule } from '@angular/common';
import { Component, ElementRef, Input, OnInit, inject } from '@angular/core';
import { loadRemoteModule } from '@softarc/native-federation-runtime';

export interface WrapperConfig {
  remoteName: string;
  exposedModule: string;
  elementName: string;
}

export const initWrapperConfig: WrapperConfig = {
  remoteName: '',
  exposedModule: '',
  elementName: '',
}

@Component({
  selector: 'app-wrapper',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './app-wrapper.component.html',
  styleUrls: ['./app-wrapper.component.scss']
})
export class WrapperComponent implements OnInit {
  elm = inject(ElementRef);
  @Input() config = initWrapperConfig;

  async ngOnInit() {
    const { exposedModule, remoteName, elementName } = this.config;

    await loadRemoteModule(remoteName, exposedModule);
    const root = document.createElement(elementName);
    this.elm.nativeElement.appendChild(root);
  }
}

app.routes.ts

import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/native-federation';
import { WrapperComponent, WrapperConfig } from './app-wrapper/app-wrapper.component';
import { config } from 'rxjs';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      loadRemoteModule('mfe1', './Component').then((m) => m.AppComponent),
  },
  {
    path: 'react',
    component: WrapperComponent,
    data: {
      config:{
        remoteName: 'react-vite-mfe1',
        exposedModule: './react-vite-mfe1',
        elementName: 'react-vite-mfe1',
      } as WrapperConfig
    }

  },
];

Any help with this regard is very much appreciated. Thanks!

manfredsteyer commented 6 months ago

Can you pls share your code?

sistla001 commented 6 months ago

Can you pls share your code?

@manfredsteyer - Thanks for your reply. Here is the sample repo I created to demo this - https://github.com/sistla001/mfe-poc

It has 3 projects 1) ng-shell - Angular shell - npm start serves on localhost:4200 2) ng-mfe1 - Angular remote - npm start serves on localhost:4201 3) react-mfe2 - react remote built using @originjs/vite-plugin-federation - npm run preview serves on localhost:4173. I used https://dev.to/abhi0498/react-micro-frontends-using-vite-30ah#setting-up-the-remote-app as an example. remoteEntry.js is http://localhost:4173/assets/remoteEntry.js

http://localhost:4200/mfe1 works! I am having issue with http://localhost:4200/mfe2

Thanks!

sistla001 commented 6 months ago

I have also created another route called mfe3 to ng-shell, this route is using app-wrapper2 instead of app-wrapper. I am passing the react_mfe2's remoteEntry.js to this and doing the following instead of using loadRemoteModule from @softarc/native-federation-runtime. This is loading the css but content is not loading, looks like some success. I will keep trying and post my research here. Thanks!

private loadRemoteModule(options: LoadRemoteModuleOptions): Promise<any> {
    return import(options.remoteEntry || '').then((module) => {
      return module.get(options.exposedModule);
    });
  }
renansoares commented 5 months ago

@manfredsteyer does native federation supports a remote built using @originjs/module-federation as in this case?

also, is it possible to create a remote using native federation and load using webpack module federation host?

baroudihassan commented 2 months ago

@renansoares , that's a good question. I'm also looking for an answer.
@manfredsteyer , can you confirm?
Thank you