Angular Component As Microfrontend In Your .NET Application

Angular Microfrontend embedded in asp .net container

Recently I investigated the concept of microfrontends in Angular. The goal I set for myself was to embed the Angular Component that I had in my standalone Angular application into one of the legacy applications that I am working on. The page, which I had to use as a container for the Angular microfrontend component, is a .NET ASPX file. ASPX pages are similar to Razor pages. You can use C# inside of them. But it’s not a pleasure :). Syntax felt weird, at least for me.

I couldn’t find “ready to use” code that would help me achieve this goal really fast. I started digging into Angular documentation and found out that it would be possible to do using Angular Custom Elements.

The solution turned out to be easy and quite elegant. It seems that it is possible to reuse Angular Components in the legacy system by using Angular Custom Elements API. This technique is one way how you can handle microfrontends by using Angular, which can be useful in some scenarios. Especially with larger teams and projects. Or when you find out that it’s just easier to do something in a new technology and just deliver it as a JS script.

This article contains an example with an Angular component embedded in ASP .NET MVC (Core 3.1) container application, but the solution is universal and will also work in any other framework, whether .NET or not. Actually, all that is needed is the possibility to attach a JavaScript file.

Creating Angular Component That Will Be Embedded As Microfrontend

We will achieve a diagram that is presented on the picture below

Microfrontend in container diagram
Angular Microfront Inside ASP .NET MVC Container

Let’s start with the basics and create an Angular Component that will be exported and used as a custom element in some other technology – ASP .NET MVC for example.

I have created a project using Angular CLI. I’ve created a really simple component that we will reuse as a custom element or a microfrontend in some containers, if you prefer that name.

<mat-grid-list cols="4" rowHeight="100px">
  <mat-grid-tile
    *ngFor="let tile of tiles"
    [colspan]="tile.cols"
    [rowspan]="tile.rows"
    [style.background]="tile.color"
  >
    {{ tile.text }}
  </mat-grid-tile>
</mat-grid-list>
Grid that has been created as Angular Component. It will be embedded into ASP .NET MVC Container as Microfrontend
HelloWorldWidgetComponent HTML result (simple grid)

We’ll use this HTML code inside Angular Component, which will be exported and used in .NET MVC Razor Page as a Microfrontend. The code generates a simple grid that is built by Angular Material Library. Let’s start!

Export Angular Microfrontend From A New Project

Let’s start with the simplest example. We’ll just embed an Angular Component, that is taking some input and displaying it. This is the scenario when you decide that you would like to use Angular with your older system, but you don’t really want to have additional dependencies such as the Angular platform browser (used to bootstrap Angular app). In this case, the Angular app can be a separate project that has its own repository. It can be even developed by other teams in your organization and provided to you as a script.

 Code samples are available here: https://github.com/lukaszreszke93/AngularMicrofronts

Preparation

So first of all, I created Angular and ASP .NET MVC (with Razor) applications.

In the Angular application, I’ve removed the app component as it will not be needed for our purposes. Our goal is to create and expose parts of applications that can be used as independent elements.

Setting up AppModule

The important step is setting up the App Module properly. In order to do that, we have to remove the old app component from declarations. We also have to add ngDoBootstrap method, which will be our new entry point into the application. When this method is called, Angular’s default bootstrap mechanism is not launched, which lets us use Custom Elements in any HTML page. We also have to provide the Injector (from @angular/core package) into the constructor.

Next, we need some components that we will define as Custom Element. I’ve created a component that is named HelloWorldWidget, using Angular CLI. HelloWorldWidget contains a simple grid that is created with the help of the Angular Material library.

The HelloWorldWidget needs to be declared in AppModule. It also has to be added into the Entry Components Array (also in the AppModule).

To be able to use custom elements, we have to install @angular/elements npm package. To do so, use the command:

npm install @angular/elements --save

We’re ready now to implement the body of our ngDoBootstrap function. In the function we have to do two things:

  • Create a Custom Element, providing injector,
  • Define the name of the custom element.
/* Imports skipped for brevity, full code is available on GitHub */
@NgModule({
  declarations: [HelloWorldWidgetComponent],
  imports: [BrowserModule, NoopAnimationsModule, MatGridListModule],
  entryComponents: [HelloWorldWidgetComponent],
})
export class AppModule {
  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    const helloWorldWidget = createCustomElement(HelloWorldWidgetComponent, {
      injector: this.injector,
    });
    customElements.define('widgets-hello-world', helloWorldWidget);
  }
}

And that’s almost it. All that’s left to do is build this up and use in our ASP .NET MVC Razor pages.

So we can use do following command:

ng build --prod --common-chunk

This will generate three files:

  • Runtime.js
  • Polyfills.js
  • Main.js

Using Angular Microfrontend in ASP .NET MVC Razor Page

The easiest solution is to copy those files into our .NET Solution. I’ve copied them to wwwroot/js/AngularWidgets.

Then, there are two things left to do:

  • We have to include scripts in the Razor page
  • We have to use the custom element tag from ngDoBootstrap that we have created earlier.
/* Index.cshtml (Razor Page in ASP .NET MVC). But the code will work in any HTML file. */
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<widgets-hello-world></widgets-hello-world>

<script type="text/javascript" src="~/js/AngularWidgets/widgets.js"></script>

That’s it. You should now see your angular component generated in your razor page (or any other HTML if you wish, it’s going to work in the same way). The result is below

Result. Microfrontend in container diagram (with application boundries highlighted)

Extending Package.json Scripts To Simplify Generating Angular Microfrontend

All right, but writing ng build --prod --common-chunk every time I need to generate output files while developing is not cool.

We can add this command as a script into the package.json file. I’ve named it build:widgets and extended it, so the three files (main.js, runtime.js, polyfills.js) are concatenated into one file, widgets.js. Then it’s enough for us to have just one script import in Razor page.

"build:widgets": "ng build widgets --prod --aot --common-chunk --build-optimizer --output-hashing=none && cat dist/widgets/runtime.js dist/widgets/polyfills.js dist/widgets/main.js > dist/widgets/reportingwidgets.js"

What’s next?

This solution doesn’t scale, right? Having to copy-paste this file for every change is painful.

It would be more useful if we could download and cache the script we generated. This would lead to a situation where client applications can be always up to date with a recent version of the script.

There are multiple ways to achieve that. One way could be to expose the files on some of your domains, for example, https://lukaszcoding.com/widget.js and to download it with a GET request on page or application initialization. This way it can be added into the script tag.

Another option that I can think of is delivering it through your CI/CD.

I haven’t done it for my project, yet. In my case, it was enough to just deliver the widget once. At least that’s what they’ve said. According to the rule “fool me once, shame on you. Fool me twice – shame on me”, I have just delivered the file into a solution where it has been used. If I will be fooled twice, then expect another blog post on how you can host your file with an Angular Component as a Microfrontend. If it doesn’t work out, I will have to adjust the course.

Summary

  • It’s possible to create an Angular Component working as a Microfrontend with Angular Custom Elements API
  • To do so, you have to give up default bootstrap and change it to a manual one
  • Output script (or scripts, if you don’t concatenate them) has to be delivered to the container
  • Delivering scripts manually into a container application works, but it doesn’t scale. The next step is hosting the output script and then downloading it from the container. The browser should take care of caching such script
  • When doing Angular as a Microfrontend, remember to set up Polyfills file correctly to provide support for older browsers (IE 11 for example)