Angular Elements: how does this magic work under the hood?

Jia Li
Angular In Depth
Published in
6 min readJun 27, 2018

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

The Angular Elements project is generating lots of hype in the community right now, and rightly so! Angular Elements provides a wealth of awesome features out of the box:

  1. Consuming Angular Elements is just vanilla HTML — no Angular knowledge required!
  2. It is self bootstrapping! Everything just works.
  3. It exports Web Components, meaning they can be used anywhere!
  4. You can still benefit from the power of Angular Framework without developing an entire site in Angular!

The @angular/elements package provides the functionality to convert Angular Components to native Web Components using the Custom Elements API. Angular Elements exposes to developers a more concise, developer-friendly, and frankly joyful way of creating Dynamic Components — it uses the same mechanism behind the scenes, but hides away all the boilerplate code.

There’s already LOTS of articles out there that describe how to create a Custom Element via the @angular/elements package. Here at Angular in Depth though, we strive to dig deeper into how Angular really works. That’s why we’re starting a series of articles investigating Angular Elements, where we’ll explain exactly how Angular implements the Custom Elements API under the hood of Angular Elements.

Custom Elements

To learn more about Custom Elements, there’s a developers.google page that really does a great job of laying out the Custom Elements API.

Here’s their one line summary of Custom Elements:

With Custom Elements, web developers can create new HTML tags, beef-up existing HTML tags, or extend the components other developers have authored.

Vanilla Custom Elements

So consider the following example. We want to create an app-hello HTML tag with the name attribute. This can be implemented using Custom Elements API. A bit further in the article, we’ll demonstrate how we can make this “name” attribute sync up with an Angular Component’s @Input. But for now, we don’t need to get into Angular Elements or mess with a ShadowDom or do anything ‘Angular’ to create a Custom Element; we can just utilize the Custom Elements API with vanilla HTML and JS!

Here’s our markup:

To implement a Custom Element, we need to implement the following hooks defined by the standard:

And here is our implementation of the Hello Custom Element:

Here is a running demo,

Let’s all take a moment to pat ourselves on the back — we’ve just implemented our first Custom Element!

In review, this app-hello tag wraps a text node, and that text node will display whatever is passed into app-hello’s “name” attribute. This is pure JavaScript.

Exposing an Angular component as a Custom Element

Now that we’ve seen all that’s involved in implementing an HTML Custom Element, let’s implement the same functionality using Angular and then make this functionality available as a Custom Element.

First let’s start out with a simple Angular Component:

As you can see, it does exactly the same as the Custom Element implemented above.

Now, to wrap it and bind to the Custom Elements we need to create a wrapper class to implement all Custom Elements’ hooks:

The next step is to bridge our HelloComponent to our HelloComponentClass. This Bridge will connect Angular Component to Custom Element.

To do this, let’s step through each of the methods required by the Custom Elements APIs and implement Angular specific binding functionality:

1. constructor()

We are going to need to initialize HelloComponent inside of the connectedCallback() method. But before we do that, we’ll need some preparatory work done here in the constructor.

By the way, if the thought of initializing an Angular Component on the fly like this sounds familiar, then there’s a good chance that you’ve read our article on Dynamic Components in Angular by our resident Max NgWizard K! This is really the exact same mechanism that we’ll follow here.

So to make our Angular Dynamic Component work (we need the componentFactory to be compiled), we’ll need to add HelloComponent to the list of entryComponents inside of our NgModule.

Basically, the work the constructor will do is:

  • it will initialize a factoryComponent based on the component definition
  • it will initialize observedAttributes based on the Angular Component’s inputs, to support things we’ll need to do in attributeChangedCallback()

2. connectedCallback()

In this callback, we’ll look to

  • Initialize our Angular Component (just like in Dynamic Components)
  • Set the Component’s initial input values
  • Trigger change detection to render the component
  • Finally, attach HostView to ApplicationRef

Here’s all that in action:

3. disconnectedCallback()

This one’s easy; we’ll just destroy the componentRef here:

4. attributeChangedCallback()

Whenever the attribute of this element is changes, we’ll need to update the respective Angular Component’s property accordingly and trigger change detection:

5. Finally, we register the Custom Element

Here is a working sample: https://github.com/JiaLiPassion/custom-element

That’s it!

That’s the idea! Using Dynamic Components in Angular, we’ve implemented Angular Elements functionality from the ground up without using the @angular/elements library!

Now don’t get us wrong — Angular Elements is awesome! All that logic we cobbled together ourselves is abstracted away in Angular Elements, and using that library leads to much better, easier to read, maintainable, and extendable code!

Here’s a quick synopsis of the modules in Angular Elements and how it relates to what we did in this article:

  • create-custom-element.ts: this module implements the Custom Element callbacks we discussed in this article, and initializes an NgElementStrategy as the bridge to connect our Angular Component to Custom Elements. Currently, we only have the one Strategy — component-factory-strategy.ts — which will use a similar process to the one we demonstrated in this article to implement this bridge. In the future, we may have other Strategies as well, and we can also implement custom Strategies ourselves.
  • component-factory-strategy.ts: this module facilitates creating and destroying component refs using a component factory. It also handles change detection in response to input changes. This is also covered in the example above.

Tune in next time to see us to explain how Angular Elements output events with Custom Events!

Acknowledgements

This article is Max NgWizard K’s idea, I made a draft structure, Zack DeRose helped me to rephrase every line of my English to make it much easier to read, and Ashnita Bali gave a lot of review comments, thank you all very much!

Thanks for reading! If you liked this article, hit that clap button below 👏. It means a lot to me and it helps other people see the story.

--

--

Jia Li
Angular In Depth

Programmer with passion, core contributor of zone.js