Getting Started with Angular Elements

Posted on March 04, 2019

Written by Preston Lamb

angular angular elements custom elements web components

Do you ever work on a non-Angular project? It could be a Wordpress theme, a JAM stack project (like this site!), or a .NET Core app, just to name a few. Now, pretend that you are working on one of those apps but need some amount of front end functionality included. You want it to work like Angular, but you can't actually write an Angular app (for one reason or another). I was recently in this situation and was pointed in the direction of Angular Elements.

Angular Elements provides a way to write custom elements in Angular that can be compiled and used on any site. If you'd like to read a little more about custom elements, go check out this site. Essentially, it's a way to define new HTML elements that can be used just like any native HTML element.

So let's see how to get started with Angular elements!

Let's start with a basic header component, one that just shows a static message wherever you use it. Maybe it's a company logo, or something you don't want to duplicate the code for over and over. In your Angular application, use the CLI to generate a component; ng g c custom-header will do. For demonstration's sake, let's say the template is simple and as follows:

<h1>My Company Header</h1>

Again, this is just something simple, but that we don't want to repeat on all our different sites. So, how do we go about using this in a non-Angular application? It's really pretty simple!

There are 4 steps for doing this: install the @angular/elements package, list your desired component in the entryComponents array and register the custom elements in the ngModule, build the project to get the needed scripts, and lastly include it in your non-Angular project.

Step 1 — Install @angular/elements

Turns out this step is pretty simple. Start by running ng add @angular/elements in your terminal. And that will do most of the work for you. The Angular CLI and schematics are incredible. The Angular and CLI teams have been incredible in that sense.

Step 2 — ngModule Work

After installing @angular/elements and building your components, the majority of the work is done in the ngModule. The first part is in the decorator. In the decorator, add an array called entryComponents and add your component from above (CompanyHeaderComponent) to that array:

@ngModule({
	...
	// Make sure to remove the bootstrap attribute
	entryComponents: [ CompanyHeaderComponent ],
})
export class AppModule {}

The next part of this step is registering the Angular element as a custom element in the constructor of the AppModule. This is pretty simple using functions from the @angular/elements package:

export class AppModule {
	constructor(private injector: Injector) {
		const companyHeader = createCustomElement(CompanyHeaderComponent, { injector });

		customElements.define('custom-header', customHeader);
	}

	// This is required so that the component will bootstrap in the project where it's included
	ngDoBootstrap() {}
}

That's really all there is to it: add the component to the header, define the custom element, invoke the ngDoBootstrap() {} method. Simple as that.

Step 3 — Build the Project

The next step is to build the application and get the resulting scripts so that it can be included in your external project. Here's a build script you can use:

ng build --prod --output-hashing=none && cat dist/project-name/runtime.js dist/project-name/polyfills.js dist/project-name/scripts.js dist/project-name/main.js > ../path/to/output/ngelements.js

That bash script builds the project, takes the needed files from the output and concatenates them into a single file, and then places it in a location that you choose. I will point out that it's important to have concatenate in the above order. If you do it out of order, the script won't work. I learned that the hard way. But that's it! Now you're ready to use it in your external project.

Step 4 — Implement your Custom Element

To implement the custom element, include the script output in step 3 in the HTML file for the project where it'll be used. Then, in the HTML, use it in the following manner:

<custom-header></custom-header>

It's amazing, but that's all it takes. With that, your custom header will now be included on the page! I know it sounds too good to be true, but that's really all it takes.

More Options

A static header is great, but we really need more flexibility. In Angular, this includes @Input()s and @Output()s. In your components, you use them the exact same as you normally would. Then register the custom elements and build the project the same as in steps two and three above. Include the resulting script in your external project, and you're ready to go.

Now, let's assume you've provided an @Input() named companyName on the CustomHeaderComponent. When you put this in the HTML, it'll look like this:

<custom-header company-name="My Company Name"></custom-header>

The value you put in that company-name attribute will be output wherever you've chosen in the component.

@Output()s are easy to deal with as well. In a new script, you need to get a reference to the custom-header element and add an event listener with the same name as the @Output(). Then, when it's triggered, you'll be able to access that event's data. Here's an example:

const customHeaderEl = document.querySelector('custom-header');
customHeaderEl.addEventListener('myCustomOutput'. (evt) => {
	console.log(evt);
});

Again, I know this looks too simple to possibly be true, but it really is this easy. I was surprised when I was learning this at just how easy it was.

** Edit **

I need to point out one more thing that I forgot to put in here when originally writing the article. For this to work, you need to do one of two things: include polyfills or change the tsconfig.json file to target es2015. If you don't, you'll do the build process and include it and nothing will happen. Don't forget this step!

Conclusion and Next Steps

I really do believe that this is the future of web development. Build custom elements and distribute them to be used by anyone on any platform, wherever they are. And Angular has made it so easy to do just that. Develop them in a familiar environment, easily build them, and easily implement them.

My next steps are to figure out how to use content projection in conjunction with custom elements. Custom elements are great, but the developer building the component will determine the UI. It would be nice if they could provide the UI while you provide the functionality. That's what content projection is for, so if we can use that here, it'll be great. If you have already done this, please reach out to let me know! Also let me know if you've used custom elements in any of your projects!

Click here to subscribe to the newsletter and be the notified when a new blog post is available!