Using Angular Elements — Why and How? — Part 1

Learn How To Build Truly Reusable Components with Angular Elements

Rajat S
Bits and Pieces

--

Angular, Google’s TypeScript based web application framework is currently at version 7 (7.2.4), with version 8 coming out in the next month or so. But I want to talk about another feature of Angular that was added to it in version 6. That feature is Angular Elements.

Angular Elements is a new package in Angular that helps us publish Angular components as custom elements. It does this by taking the Angular component and compiling it into a web component

In this post, I will show you how to use this package in your Angular application. But first, let's take a look at why you should use Angular Elements.

Tip: Building with components? Use Bit to organize share and reuse components between apps with 0 overhead. It’s open source.

Why should we use Angular Elements?

This is a question that every developer should ask themselves, and not just about Angular Elements, but about every other dependency, framework, and third-party library that they want to add to their project. After all, there are thousands of libraries out there and if start adding them to our app just because they are available to us, then your app’s size will be humongous for no reason at all.

Coming back to Angular Elements, here are some of the reason why I think it will make a great addition to your Angular project.

#1. Reusability to a Whole New Level

With Angular Elements, we can make our components truly reusable. Meaning, you can use Angular Components in other frameworks and libraries, such as React, Vue, and Ember!

#2. Use Angular in the Server-Side

With Angular Elements, you can add Angular, which is famously known as a front-end framework, to your project’s backend! Is that cool or what!?

#3. Publish Parts of the Application

Angular Elements also allows you to develop and publish parts of the application independently!

These are just some of the things that first drew me towards Angular Elements. I will show you some more reasons why Angular Elements is great in the following sections, where we see how to use it.

Getting Started

Let’s start by creating a new Angular Project in our system. For this, make sure that you have the latest version of Angular CLI installed. Run the following command in your terminal:

$ npm install -g @angular/cli

Once this install is complete, we can use it to create a new initial application in our system like this:

$ ng new angular-app

You will then asked to select the additional features that you may want to add to your Angular project, things like routing and the type of stylesheet format. Select the default answer by hitting the Enter/Return key.

The CLI installs the necessary Angular packages and dependencies in a folder called angular-app. You can serve this application on to the browser by running the following commands:

$ cd angular-app
$ ng serve --open
// --open flag will open the browser for you

As you can see, the app contains already contains a few things that we can display on the screen. But we don’t need them. So go back to your code editor (I ❤️ VS Code) and delete everything inside src/app/app.component.html file and write a single <h1> element.

<h1>Welcome To Angular Elements!</h1>

If you take a look at the file structure, you will see that each component in angular consists of a multitude of files. For the App component alone, there is a .ts, .html, .css, and a spec.ts file that contains the tests, and a module.ts file. There will also be an optional routing.module.ts file if you had chosen to add the routing feature to the application while creating it using the CLI.

Instead of writing all these files for our components, Angular CLI gives us a shorthand method that will generate these files for us. Let’s use this method to create a HelloWorld component in our system.

$ ng g c HelloWorld

This single command will create a folder named hello-world inside the app folder. If you open this file, you will find these 4 files inside it:

  • hello-world.component.css
  • hello-world.component.html
  • hello-world.component.spec.ts
  • hello-world.component.ts

We will only be using the .html and .ts file. There are additional flags that we can pass to the ng g c command. But in order to keep things simple and to point, I won’t use them. Check Angular Docs to know more about how to use this command.

Inside the hello-world.component.ts file, you will see that the selector is set to app-hello-world. So, if you add this to the app.component.html like this:

<h1>Welcome To Angular Elements</h1>
<app-hello-world></app-hello-world>

Then the contents of hello-world.component.html will be displayed in the browser.

Our initial setup is now ready. Let’s get started with how to use Angular Elements.

Convert A Component Into Custom Element

Right now, we have a simple HelloWorld component that simply prints some text on the screen. Let’s take a look at how we can insert this component into the app in a more dynamic fashion. To do this, let’s replace the app-hello-world with a div and a button. Let’s also add the click listener to the button, so that when someone clicks the button, the HelloWorld component can be displayed. This will be handled by a showMessage() function. So go to the app.component.html page and make these changes:

<h1>Welcome To Angular Elements</h1>
<div id="hello"></div>
<button (click)="showMessage()">Display?</button>

We then need to define the showMessage() function in the app.component.ts file. Here we will use regular DOM manipulation to get the div by selecting it using its id of hello. Then, we will apply the innerHTML function to insert the app-hello-world component like this:

export class AppComponent {
showMessage() {
const hello = document.getElementById('hello');
hello.innerHTML = '<app-hello-world></app-hello-world>';
}
}

You can go to your browser and click on the button, but I will tell you right now that nothing is going to happen. But then again, if you go the browser’s dev tools and look there, you will see that the HelloWorld component is inserted.

The reason why we can’t see the component on the screen is that Angular cannot recognize the app-hello-world tag since it has been inserted into the template dynamically. Angular needs all the elements to be present in the template, or you need to use the dynamic component factory to instantiate the component at runtime through your code.

This is where Angular Elements comes into play. By using Angular Element, all we need to do is insert the tag dynamically, and Angular itself will instantiate the component for us. Let’s finally get to see how to use this component in our app. First, use the ng add command to install Angular Elements into our app:

$ ng add @angular/elements

Once you have this installed, go to app.module.ts and make the following changes to the @NgModule:

import {NgModule, Injector} from '@angular/core';
import {createCustomeElement} from '@angular/elements';
@NgModule({
declarations: [AppComponent,HelloWorldComponent],
imports: [BrowserModule,AppRoutingModule],
entryComponents: [HelloWorldComponent],
providers: [],
bootstrap: [AppComponent]
})

Then in the AppModule class, write a constructor function create a new instance of the createCustomElement and pass it the HelloWorldComponent as shown below:

export class AppModule {
constructor(injector: Injector) {
const custom = createCustomElement(HelloWorldComponent, {injector: injector});
}

}

Along with the component, we also have to pass it the injector. The injector is something that we use to resolve the dependencies. With that, we have created an instance of the HelloWorldComponent as a custom element. We can then use the customElements API to define the component’s tag as app-hello-world.

customElements.define('app-hello-world', custom);

Go back to the hello-world.component.ts file and remove/comment the selector inside the @Component section. This way, Angular will not get confused and will know to use the custom element that we just created.

Rerun the ng serve --open command to make sure that Angular takes in all the changes that we have just made.

There is a good chance that the button still does not displays the HelloWorldComponent. This may be because Angular “fails to construct the HTMLElement”. To solve this issue, install the webcomponentjs polyfills:

$ yarn add @webcomponents/webcomponentsjs

Then, go to the polyfills.ts file and import the following:

import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js';

Finally! Our custom element will get displayed on the screen when we click that button.

Compiling as a Standalone Element

Another benefit of creating custom elements is that they can be compiled into standalone script files that can then be added to different frontend frameworks. This truly makes it Angular Elements as a powerful toolkit. Lets take a look at how we convert our custom element into a standalone script file.

Create a new folder named preview in the Angular project’s root directory. And inside this folder, create a new file named index.html and write the following code inside it:

<html>
<body>
<app-hello-world></app-hello-world>
</body>
</html>

Then, go to app.module.ts and remove the AppComponent from the declarations field from @NgModule. Also remove the boostrap field completely and add a ngDoBootstrap function inside the AppModule class to tell Angular to handle the bootstrapping for us.

export class AppModule {
constructor(injector: Injector) {
const custom = createCustomElement(HelloWorldComponent,{injector});
customElements.define('app-hello-world', custom);
}
ngDoBootstrap() {}
}

Since we don’t need the AppComponent anymore, we delete all the files related to it. So go ahead and remove the app.component.css, app.component.html, app.component.spec.ts and app.component.ts files. Be careful, and do not delete the app.module.ts file, and also remove the app.routing.module.ts file which you get if you have opted to add the routing to your app.

After all the deleting, this is how your app’s folder structure should look:

We can now set up a custom build process for our HelloWorldComponent. Angular CLI already has a build process ready for our application, that will follow the angular.json file and compile the project’s production build.

So open up your console, and run the build script.

$ npm run build

You will now see a dist folder created in your project directory, and it contains a whole lot of files inside it. We need to concatenate all of these files into a single script which we can then import into the HTML file from before.

To do all this, lets create a simple file called custombuild.sh inside the root folder. A .sh file is a shell script file and it will contain the command that will combine all the files.

#!/bin/sh
ng build angular-app --prod --output-hashing=none && cat dist/angular-app/runtime.js dist/angular-app/polyfills.js dist/angular-app/scripts.js dist/angular-app/main.js > preview/angularapp.js

That’s one big command! Let’s make sense of it by breaking it down. First, we want the command to run ng build script from the Angular CLI. We then give it the name of our Angular app, which in this case is angular-app. Then we tell the CLI to give us the production build, and also say that we don’t want any output-hashing.

Then using the cat command, we are concatenating the runtime.js, polyfills.js, scripts.js, and main.js files from the dist/angular-app folder into a angularapp.js file inside the preview folder.

If you are a Mac or Linux user then you can easily run this file inside the terminal like this:

$ ./custombuild.sh

But if you are a Windows user like me, then you will face some problems while running the above command in your terminal. This is because the Windows OS does not come with Bash support.

The quickest support to this is to become a Git user. By installing Git, you also get the Git Bash program, and you can run the program inside it like this:

You will then notice a new file named angularapp.js created inside the preview folder, with a whole lot of confusing JavaScript code inside it. All this code describes our HelloWorldComponent, we can now insert it into the index.html file using the script tag like this:

<html>
<body>
<app-hello-world></app-hello-world>
<script src="./angularapp.js"></script>
</body>
</html>

Finally step — Running just this component on the browser. For that, we can use the npx tool to run the live-server utility to deploy the component on localhost:8080.

$ npx live-server preview

Communicating with Angular Elements

Right now, our HelloWorldComponent is nothing but a single line of text that we got when we created the component using the CLI. Let’s make some changes to it so that it resembles an actual component instead.

First, go to the hello-world.component.html file inside src/app/hello-world folders and write the following HTML code:

<div>
<h1>{{title}}<h1>
</div>

Then go to the hello-world.component.ts file and import the Input decorator from @angular/core.

import { Component, OnInit, Input } from '@angular/core';

This decorator is used to control the inputs to the Angular app. We have multiple inputs that we have written in the HTML file, but we still need to define them inside the hello-world.component.ts file like this:

export class HelloWorldComponent implements OnInit {
@input() title;
@input() rname;
@input() occupation;
@input() location;
@input() first;

constructor() {}
ngOnInit() {}
}

I have created these extra inputs for the future purposes, but our component is now ready! Let’s build it just like we did before by running this command inside a bash shell:

$ ./custombuild.sh

Once this is done, serve the component onto the browser using live-server:

$ npx live-server preview

But the page is empty! Where is the title? This emptiness is because we have not given any values to our title input. Let’s take care of this in the index.html file that is inside the preview folder.

<html>
<body>
<app-hello-world
title="Batman
></app-hello-world>
<script src="./angularapp.js"></script>
</body>
</html>

Going back to the browser and you will see the title displayed!

Our component can now take in inputs, but what about events? An event such as a user clicking on a button requires an Output decorator. So let's import it into the hello-world.component.ts file. Lets also import the EventEmitter from @angular/core:

import {Component, OnInit, Input, Output, Event} from '@angular/core';

Then create a new EventEmitter function called display inside the HelloWorldComponent class.

@Output() display = new EventEmitter();

In order to trigger this EventEmitter, we can create a button element inside the html element like this:

<button (click)="showInfo()">Display</button>

If the user clicks on this button, then the showInfo function will be executed. Let’s go back to the TypeScript file and define this function like this inside the HelloWorldComponent class:

showInfo() {
this.display.emit(`Name: ${this.rname}
Occupation: ${this.occupation}
Based In: ${this.location}
First Appearance: ${this.first}`);
}

Go ahead and rebuild the component inside the bash shell using the ./custombuild.sh command. Then go to the index.html file and add values for the remaining inputs that we had created earlier:

<app-hello-world
title="Batman"
rname="Bruce Wayne"
occupation="World's Greatest Detective"
location="Gotham"
first="Detective Comics #27"

></app-hello-world>

All that’s left to take care of is displaying this values. Let’s shake things up and display in the form of an alert when the user clicks on that button. First, create a script section in the index.html file and define grab the app-hello-world component using the querySelector.

Then, we can use the addEventListener to listen for our custom event display. When the app hears this event, we can tell it to display its detail inside an alert.

<script>
const component = document.querySelector('app-hello-world');
component.addEventListener('display', (event) => {
alert(event.detail);
}
</script>

The final product should be this:

More?

Until now, we have seen a whole lot of things that Angular and Angular Elements can do for us.

But what if I told you that Angular Elements can do even more? 😲

With Angular Elements, we can:

  • Put Angular Components inside other JavaScript Libraries/Frameworks like React and Vue.
  • Pass data from React and Vue into the Angular Component
  • Use Angular 7 component into an AngularJS app. If you are an Angular developer, then you know how different Angular and AngularJS are!

But this post has already become really long. So let’s continue exploring the uses of Angular Elements in the Part 2 of this series:

Thank you for reading this really, really long post. I hope it helped you understand how to use Angular Elements in your Angular applications. Please feel free to comment here or hit me up on Twitter if you have any thoughts to share with me! Cheers 🍺

--

--