Modernization

Micro-Frontend Architecture Part 2: The Implementation with Angular and Web Components

Lucas Fievet
|
April 12, 2022

Last week we have introduced you to micro-frontends and discussed the pros and cons that come with micro-frontend Architecture. As part 2 of the blog, we are going to implement components in a distributed manner and face the issues that can come up during the process.

As we have discussed in the previous blog, web applications these days are becoming more and more complex. If some sections and features of your frontend are developed by different teams then the issues can present themselves in the form of bugs, interface changes, new features, etc.

The solution to that Web components, where you create each widget as a web component, every one of them can be deployed independently as a .js file. Thus, applications are rendered and loaded accordingly to the created layout.

The Implementation

We are going to create two applications, one is an Angular app that can be one of many applications in your set, so let’s call it a WrapperApp. Some dependencies need to be installed and we will need to consider how to load components.

The second app will be called WidgetComponent. We are going to use some libraries in order to customize the usual Angular build with needed considerations.

WrapperApp

It will be a simplified example, there will be no layout templates to solely explain how it works.

In this blog, we will be using an empty angular app made with the Angular-CLI, but any other framework can be used to create this application, like Vue, React, etc. (either javascript or web application).

In this instance, we are going to set some primary outputs and inputs for the loaded web component. You need to consider that, if you need to communicate within the web components, you need to define an interface in a way that the contract between the two applications is clear.

However, you can also include breaking changes in the web component, but then an update in the main app needs to be done as well. Lastly, you need to ensure that you have a strategy for the URLs where the web components are going to be deployed as well as defining a naming convention for the .js file. This is needed so that you can deploy new versions with breaking changes without affecting all your applications.

About the dependencies

You need @angular-extensions/elements. It is an angular npm library that reveals a directive that is going to help to load, as well as render the web components. Custom directives can be created as well but we find that with @angular-extensions/elements the development process is far simpler.

About the code

There is the official documentation of @angular-extensions/elements, which you can access via https://angular-extensions.github.io/elements/#/home. However, here is a simplified example.

  1. First run: npm i @angular-extensions/elements
  2. Then import { LazyElementsModule } from '@angular-extensions/elements' and add it to the imports of your AppModule
  3. Then you add the schemas property to the @NgModule decorator of your AppModule and add the CUSTOM_ELEMENTS_SCHEMA
  4. Lastly, with *axLazyElement directive you can load the element you need and enter the URL of the element bundle

The app.module.ts file shows how it is put together:

Use your app-cover component in app-component and just like that, and your widget component will be loaded. Important to notice: your event data of bound Events will be located under $event.detail
So let's create that widget now.

WidgetApp

Our example will be using a simple component, however, you can choose any complexity you wish and have assets as well as request data from an API. We recommend using absolute paths as the component will be running inside the WrapperApp, thus you need to be cautious with relative URLs as they are very crucial for the production and local environments.

It is recommended to use WebComponentBaseClass which has default events and attributes you need for all your web components. With this class, you can set a baseUrl. This can be helpful in production as well as in local environments.

Lastly, with the inputs and outputs, you can have communication between web components. However, one of the issues that can arise is complex logic due to the asynchronous loading of the web components. Global services that share data, configurations, and more, should not be accessible to the component unless you use to add them as inputs.

About the dependencies

  1. @angular/elements - It’s an angular library that contains the methods for converting a component into a web component, add it with ng add @angular/elements
  2. serve - Used to serve the javascript file in your dist folder. Add it with npm i serve --save-dev as a dev dependency
  3. @webcomponents/webcomponentsjs (optional) - A polyfill, will not be vital unless your target in tsconfig.ts is ES2015
  4. ngx-build-plus - It extends the Angular CLI's default build behaviour. It provides the functionality to build the whole project into one .js file, which is needed to implement a web component. Install it via npm i ngx-build-plus --save-dev

About the code

There is a section on the creation of web components, that is called Angular Elements. You can access it via https://angular.io/guide/elements .

But before it is important to note that firstly, NgModule does not have the definition of the Bootstrap component. Moreover, in the ngDoBoostrap, the “widget-component” tag name of your choice should be exactly the same as the name you will use in the wrapper app with the axLazyElement directive.

With the given guide, the AppModule of the WidgetComponents App will look like the following:

As you have seen in the WrapperApp our widget-component should be able to handle bindings of inputs and outputs, so here is our minimal WidgetComponents Typescript file with an inline template:

For the ngx-build-plus configuration, you should replace the default builder with the ngx-build-plus in the angular.json file:

Then you should add two custom scripts in your package.json file. If they are already present, just replace them

The initial one should come to play during the development and the last one should be there to build the component. The continuous deployment tool can utilize it and at the same time makes sure that the name is fixed as well so that the build results are in a single file, therefore you can load the exact same file name.

Conclusion

We have only covered Angular in this blog but many other techs can be used in a single application. Don’t forget that each and every web component has to be implemented as an individual Angular app, but if you want a library of visual components, Angular Library can be used where you can share the components as npm modules.

Also, the demonstrated build process slightly varies from the traditional Angular projects, thus you should be aware of that when configuring the automated process. Lastly, to deploy the web components, a consensus is needed. For instance, it could be deployed as {environment}/{webcomponentname}/main.js.

Related Blogs Posts

Bring your productivity to the next level

Reach the automation level you are aiming for

Leverage no-code and write custom code where needed

Visualize your end-user experience all in one place

Make your deployment decisions easy

Get Started