I have created a microfrontend(mfe) in Angular using module federation.
Below are the steps that I used to create the mfe:
- I have run: ng new mfes –standalone false
- After navigating in the ‘mfes’ directory, I ran the command: ng generate application product-listing.
- I navigated in product-listing and ran the command: npm install @angular-architects/module-federation.
- A file called webpack.config.js was created.
I modified the content of the file as follows:
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, '../../tsconfig.json'),
[/* mapped paths to share */]);
module.exports = {
output: {
uniqueName: "productListing",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
experiments: {
outputModule: true
},
plugins: [
new ModuleFederationPlugin({
library: { type: "module" },
// For remotes (please adjust)
name: "productListing",
filename: "remoteEntry.js",
exposes: {
'./ProductListingModule': './projects/product-listing/src/app/app.component.ts',
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
The folder structure of the ‘product-listing’ mfe is below:
In a different folder, I have created a host app which I called main-app, using the command: new new main-app –standalone false. I have also added module federation.
Below is the config in the file webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
const share = mf.share;
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(
path.join(__dirname, 'tsconfig.json'),
[/* mapped paths to share */]);
module.exports = {
output: {
uniqueName: "mainApp",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases(),
}
},
experiments: {
outputModule: true
},
plugins: [
new ModuleFederationPlugin({
library: { type: "module" },
// For hosts (please adjust)
remotes: {
"productListing": "productListing@http://localhost:3000/remoteEntry.js",
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
};
Below is the folder structure:
I have also added the route for this mfe in the app.module.ts. Please find the file below:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {RouterModule, Routes} from "@angular/router";
import { loadRemoteModule } from '@angular-architects/module-federation';
const routes: Routes = [
{ path: 'product-listing', loadChildren: () => loadRemoteModule(
{
type: "module",
remoteEntry:"http://localhost:3000/remoteEntry.js",
exposedModule: './ProductListingModule'
}).then(m => m.ProductListingModule) },
];
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
RouterModule.forRoot(routes),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I am serving the mfe ‘product-listing’ and can be accessed via localhost:3000
I am running the main-app (host app) and it can be accessed via localhost:4000.
However, when I am accessing localhost:4000/product-listing, I am getting the following error:ERROR TypeError: Cannot read properties of undefined (reading 'ɵmod')
in the console. The URL is also changed back to localhost:4000.