helencousins.com

Mastering Module Federation with Angular Standalone Components

Written on

Chapter 1: Introduction to Module Federation

This guide serves as the second installment in our exploration of Module Federation. Here, we delve into the practical application of Module Federation with Angular Standalone Components. The objective is to provide a comprehensive walkthrough that outlines the integration of this functionality within an Angular application. Our discussion will be based on a newly created Angular application where we will progressively implement module federation.

The examples and code showcased herein are developed using Angular version 17. You can access the source code for this application [here](#).

Step 0: Overview of Our Application

Our project revolves around a basic notes application featuring two primary tabs: Notes and Customer. In this setup, the application that hosts both tabs is referred to as the shell or host application. The Customer tab, however, operates as a separate remote application. The host application utilizes module federation to dynamically load and integrate the customer application.

  • Host: http://localhost:4200
  • Remote: http://localhost:4201

The host application comprises the notes functionality while consuming the customer app as a remote entity. Feel free to experiment with your applications or utilize the provided source code for a hands-on experience.

Step 1: Custom Webpack Configuration

To facilitate module federation, we employ a plugin known as ModuleFederationPlugin from Webpack, which is not included by default in Angular applications. Therefore, our first task is to configure the application to accept custom Webpack settings, allowing us to incorporate the module federation plugin.

We will utilize the custom-webpack builder to implement this customization. Begin by installing the necessary package:

npm i -D @angular-builders/custom-webpack

Next, apply the following modifications:

  • Change the build's builder to custom-webpack:browser
  • Change the serve's builder to custom-webpack:dev-server
  • Specify the path to the custom Webpack configuration file in the options under customWebpackConfig
  • Modify the "browser" key under options to "main"

"architect": {

"build": {

"builder": "@angular-builders/custom-webpack:browser",

"options": {

"main": "./src/main.ts",

"customWebpackConfig": {

"path": "./custom-webpack.config.js"

}

}

},

"serve": {

"builder": "@angular-builders/custom-webpack:dev-server"

}

}

This configuration is required for both the host and the remote applications. However, the remote application needs an additional setting to address issues that arise when both the host and remote are served simultaneously using ng serve.

"architect": {

"serve": {

"options": {

"publicHost": "http://localhost:4201"

}

}

}

If the above option is not configured, the host application may experience frequent refreshes as it continuously fetches updates from the remote.

Step 2: Configuring Webpack for Remote

In this step, we will add the module federation plugin to the custom Webpack file for the remote application. While additional configuration options will be included, comments will clarify their purpose to maintain brevity.

The remote application will expose functionalities for other applications to utilize:

  • name: A unique identifier for this module federation application.
  • filename: The entry file generated for the remote, which the host will use to access the exposed modules.
  • exposes: The modules we wish to share from this application, with the key being the name assigned to the exposed module and the value representing the module's path.
  • shared: The libraries we intend to share with other applications. Initially, this can be left empty, as we will populate it later.

// custom-webpack.config.js

const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {

plugins: [

new ModuleFederationPlugin({

name: "angular-mfe-remote", // Unique name for the remote application

filename: "remoteEntry.js", //

exposes: {

"./Test": "./src/app/customer/customer.component.ts",

},

library: {

type: "module",

},

shared: {}

}),

],

}

Once this step is completed, executing the npm run build command will generate a remoteEntry.js file as part of the build process. This concludes the setup for the remote application, with module federation managing the underlying operations.

Step 3: Configuring Webpack for Host

Next, we will replicate the previous step for the host application so it can consume the modules exposed by the remote.

The host's configuration mirrors that of the remote, but instead of exposes, it will include remotes:

  • remotes: The modules that the host intends to consume. The key is the name given by the host to the remote, and the value is the URL of the remote's entry file.

const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {

plugins: [

new ModuleFederationPlugin({

name: "angular-mfe-host",

filename: "remoteEntry.js",

remotes: {

remote: "http://localhost:4201/remoteEntry.js",

},

shared: {},

library: {

type: "module",

},

}),

],

};

Step 4: Adapting Bootstrap Pattern

In Angular projects, the main.ts file serves as the entry point and contains the code necessary to bootstrap the application. However, we need to modify this for module federation, as shared libraries may not be available if we load and execute our main application immediately. Therefore, we will transfer the code from main.ts to a new file named bootstrap.ts. The main.ts file will then dynamically import bootstrap.ts, allowing sufficient time for the application to load the remoteEntry.js file and determine the shared libraries.

// main.ts

import('./bootstrap').catch((err) => console.error(err));

If you attempt this, you might encounter the following error:

Error: Shared module is not available for eager consumption

An alternative solution involves eagerly loading libraries in the shell and including them in the main build, although this approach is not typical and will be discussed in a future article.

Step 5: Accessing Remote from Host

With everything set up, we can now access the remote application from our host. We will utilize JavaScript's dynamic import feature to load the application, similar to how we incorporate other modules into an Angular application.

export const routes: Routes = [

// Local module

{

path: 'home',

loadComponent: () =>

import('./notes/notes.component').then((m) => m.NotesComponent),

},

// Remote module

{

path: 'customer',

loadComponent: () =>

import('remote/Customer').then((m) => m.CustomerComponent),

},

];

The pattern for importing the remote module is as follows:

import(${LOCAL_NAME_BY_HOST}/${REMOTE_EXPOSED_MODULE});

You will see how straightforward and seamless it is to access the remote module using module federation. However, an error may arise during the import:

Cannot find module 'remote/Customer' or its corresponding type declarations.

To resolve this, it is customary to declare a file at the root level named remotes.d.ts, where we can specify the remote types. The contents of this file would be:

declare module 'remote/Customer';

This concludes the setup for facilitating communication between the applications. Start both applications using npm start and navigate to the URL http://localhost:4200 or your application URL.

Step 6: Addressing Multiple Angular Version Issues

You may notice some console errors when you run the application. Don't worry; these are intentional to illustrate specific concepts.

The error message you may encounter in the host application is as follows:

As we are not sharing any libraries, the @angular/core library will be loaded multiple times in the browser. Since Angular does not function well with multiple versions loaded simultaneously, we must ensure that the core library is shared between applications.

To resolve this, add the following configuration to the Webpack's shared settings that we initially left empty. Restart both applications afterward.

shared: {

"@angular/core": {

singleton: true,

strictVersion: true,

requiredVersion: "^17.1.0",

},

},

And just like that, your applications will function correctly. It is also wise to share other Angular libraries to ensure stability. Thus, I will also include the router and common libraries in the shared configuration:

shared: {

"@angular/core": {

singleton: true,

strictVersion: true,

requiredVersion: "^17.1.0",

},

"@angular/common": {

singleton: true,

strictVersion: true,

requiredVersion: "^17.1.0",

},

"@angular/router": {

singleton: true,

strictVersion: true,

requiredVersion: "^17.1.0",

},

}

Thank you for taking the time to read through this extensive guide. We hope you have gained valuable insights. Please consider subscribing for more content in future articles.

Chapter 2: Practical Video Tutorials

To further enhance your understanding of Module Federation with Angular, we have included some video resources.

The first video, titled "Experimenting with Module Federation & Angular Standalone Components in Nx," provides practical examples and insights into the concepts we've discussed.

The second video, titled "Angular Module Federation Micro-FE Speed Run," offers a rapid overview of the module federation process in Angular applications.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Top User Models in Leonardo AI: Create Unique Images Effortlessly

Explore top user models in Leonardo AI to generate stunning images using community-driven creativity and unique styles.

The Human Body: Nature's Most Remarkable Machine

Explore the complexity and wonders of the human body, our most extraordinary machine, and how it compares to man-made devices.

Exploring the Cosmos: Season Two of the Mission: Interplanetary Podcast

The Mission: Interplanetary Podcast returns with exciting topics and guests, exploring space's mysteries and challenges.

Transforming Mediocrity into Marvelous Opportunities

A personal journey from a dead-end job to discovering joy and purpose in life.

Unlocking the Potential of Your First Retrospective of the Year

Discover how to transform your first retrospective of the year into a powerful tool for team growth and success.

Embrace Responsibility: The Key to a Fulfilling Life

Explore the importance of personal responsibility and how it can lead to a more fulfilling and empowered life.

How Mother Nature Helped Us Overcome the Pandemic Challenge

A look at how Omicron may have served as a natural vaccine against COVID-19.

Exploring the Depths of Sci-Fi Microfiction: Four Distinct Tales

Delve into four captivating sci-fi microfiction stories exploring themes of identity, consciousness, and the impact of technology.