Creating Angular Web Components: A Micro Frontend Tutorial
It is common to find that the tech stack you choose to solve one problem does not work to solve another. Maybe your company is big enough to have different dev teams, each with a unique knowledge and focus on specific technologies. For example, you may have three teams that are familiar with React and three more teams that focus on Angular web components. So how do you choose which team to give new projects to?
With micro frontends, you can combine different technologies within the same application, allowing for collaboration across different teams without forcing one to learn a new framework/language.
In this tutorial, we’re going to learn how to set up an Angular application to host different micro frontends and configure it as a web component capable of running in any browser and/or inside another Angular or React application.
Some key reasons to choose micro frontends over the traditional monolithic architecture are:
• The companies have different dev teams each one with specific knowledge for certain frameworks
• Multiple applications that can reuse the web component (ie. Login tab or Chat window)
• The main application can continue running even if the internal web component fails
The majority of the projects in the front end are monolithic. With micro frontend architecture, we can divide the web app into different web components, independent of each other, each one with its own development, test, and delivery processes.
This web architecture is tech agnostic, meaning every component can be written using a different framework, like Angular or React, enabling multiple teams to work in the same project by using the technology that best suits the component/feature. This helps increase browser compatibility, speed up the update, deploy, and resolve bugs without compromising the main app.
Let’s look at an example. Facebook uses this kind of architecture on its website (see post):
As you can see in the picture above, the chat function and news feed are different main window components. The main goal of micro frontends is to break the complete website into different web components that don’t affect the user experience, allowing the user to start interacting with the individual components as they load on the page.
Now that we have an idea of how micro frontends work, let’s dive into a tutorial for creating Angular web components.
Creating an Angular Web Component with Ngx-build-plus
Step 1: Install Angular Cli
npm install -g @angular/cli
Step 2: Create a new project ({project-name}-wc-feature)
ng new phoenix-wc-wiki-ngx
When the terminal asks us, “Would you like to add Angular routing?” we choose “No.”
Step 3: Add the @angular/elements and ngx-build-plus
ng add @angular/elementsng add ngx-build-plus
*To check if the installation was successful, go to the angular.json and check the property “builder” under architect → build, should be “builder”: “ngx-build-plus:browser”.
Step 4: Comment the bootstrap property in the @NgModule declaration`, add the AppComponent to entryComponents, and add the custom element in phoenix-wc-wiki-ngx/src/app/app.module.ts
* To be able to use the AppComponent with ng serve and develop normally, you have to uncomment the bootstrap property in the @NgModule declaration phoenix-wc-wiki-ngx/src/app/app.module.ts. But do not forget to comment it when you are going to generate the build.
Step 5: Run the command to generate the build
ng build --prod --output-hashing none --single-bundle true
*If you get the following error: Schema validation failed with the following errors:Data path “.budgets[1].type” should be equal to one of the allowed values.
Step 6: Go to budgets in the angular.json and delete these lines:
Leaving only:
Step 7: Install Static-Server to test the component
npm i -g static-server
In the folder dist/phoenix-wc-wiki-ngx, create an index.html, adding the zone.min.js needed to run angular, webcomponents-bundle.js and custom-elements-es5-adapter.js, and polyfill.js to make it cross-browser compatible.
Step 8: Run the command in the folder dist/phoenix-wc-wiki-ngx
Go to http://localhost:9080/ and you should see:
static-server
Step 9: Repeat the same process to create another web component called phoenix-wc-wiki-ngx-lt
Step 10: Now we have two web components. To test them together, change some of the build files from main-es5.js to{project-name}-bundle.js.
Step 11: To test that both web components are working together, add the files *bundle.js to the same folder and change the index.html use for the static-server like this:
If you refresh the http://localhost:9080/, you will see something like:
You can see that now we have both web components on the same page.
Adding Initial Parameters to the Web Component
Let’s add a web component input parameter so that we can get data from outside for the creation of the web component.
Step 12: In the src/app/app.component.ts for the phoenix-wc-wiki-ngx-lt project, we’re going to add an input element called “token.” For this, we have to add the functions ngOnInit and ngOnChanges to handle incoming data:
Step 13: After generating the build, we can add the new value to the declaration of the web component.
<phoenix-wc-wiki-ngx-lt token="token Value Test"></phoenix-wc-wiki-ngx-lt>
Step 14: Let’s hit refresh and open the browser console:
Using Custom Events to Communicate Data Between Web Components and Container App
What if we need to send data from one web component to another?
To accomplish this, we’re going to use Custom Events. This communication interface provides us with clean, cross-platform communication between the web components for different technologies.
Custom Event Dispatcher
Step 1: In the src/app/app.component.html for the project phoenix-wc-wiki-ngx, let’s create a button with the function sendCustomEvent on click.
<button (click)="sendCustomEvent()">Custom Event</button>
Step 2: In the src/app/app.component.ts, add the function sendCustomEvent with the dispatcher of the custom event.
The name of the custom event {project-name}-ce-{action}
Step 3: Now that we have the dispatcher, we’re going to add the listener to the web component phoenix-wc-wiki-ngx-lt.
Custom Event Listener
Step 4: In the src/app/app.component.ts:
• Create a custom event function in charge of handling the data and call from the custom event (to handle de data use event.detail)
customEventListenerFunction(event) {console.log('testfunc - ' , vent.detail);}
• Add the listener to the ngOnInit function to the custom event function
window.addEventListener('phoenix-wc-wiki-ngx-ce-data-sent-test', this.customEventListenerFunction, true);
• Implement the function ngOnDestroy
implements OnInit, OnChanges, OnDestroy {
• Add the remove listener to the ngOnDestroy
ngOnDestroy():void{window.removeEventListener('changeNameToCustomEvent', this.customEventListenerFunction, true);}
After we generate the builds and hit refresh to http://localhost/9080/, we can see in the console, if we hit the button in the first web component, how the phoenix-wc-wiki-ngx-lt-bundle.js executes the function added as a listener.
Lazy Loading Web Components
Step 1: Let’s add @angular-extensions to the project.
npm i --save @angular-extensions/elements
Step 2: Append LazyElementsModule to the imports: [] of your AppModule
Add new schemas: [] property with CUSTOM_ELEMENTS_SCHEMA value to @NgModule decorator of your AppModule
After this, we can use the *axLazyElement directive to lazy load the web components.
Step 3: Add a variable with the URL of the location of our web component.
elementFUrl = 'assets/elements/phoenix-wc-wiki-ngx';
Or
elementFUrl = 'https://cdn.ourwebhosting.com/1.2.3/elements/phoenix-wc-wiki-ngx';
Step 4: In the template .html, add the *axLazyElement directive.
<phoenix-wc-wiki-ngx *axLazyElement="elementFUrl"></phoenix-wc-wiki-ngx>
ZoneStrategy
Sometimes the parent application is an Angular app. To avoid ngzone conflicts, we’re going to use ElementZoneStrategyFactory when we instance the web component.
Step 1: Just change the ngDoBootstrap in the app.module.ts:
ngDoBootstrap() {const strategyFactory = new ElementZoneStrategyFactory(AppComponent,this.injector); const eventsElement = createCustomElement(AppComponent, {injector: his.injector,strategyFactory}); customElements.define('phoenix-wc-wiki-ngx', eventsElement);}
To conclude, the micro frontend architecture allows companies to give their dev teams absolute control over their knowledge, the freedom to mix different technologies by taking advantage of the strengths of each framework to solve different problems, and allows for the reuse of web components in multiple applications. However, it is important to keep in mind that the more web components with different technologies you have, the more dev teams you’ll need to maintain the code and deployment if you have CI/CD in your development process.
I hope this gives you a clear picture of what a micro frontend architecture is, how to use it in Angular, and the reasons why this new way of creating web applications can benefit your dev teams and your business as a whole.