In my recent article about Angular Elements - “Building a Custom Element Using Angular Elements“, we introduced with an experimental Angular Labs project - Angular Elements and explained the motivation for this project. For demonstration purposes only, we developed an Angular component that displays a customizable “Made With Love” message and exposed it as a custom element using Angular Elements. In order to finish the demonstration, we used this custom element in a React project.
I advise you to go over my recent article (if you haven’t read it yet) - it’ll give you a sense of the concept in general.
This article is going to focus on the practical side of Angular Elements, considering the final changes that were made and compared to the experimental project which was examined as part of Angular Labs.
How to Install
The sixth version of Angular includes a new scoped package which’s named
Just as a regular npm package, we install it using our favorite package manager:
npm install @angular/elements
This package provides the necessary tools for creating a custom element based on an Angular component.
In addition, Custom Elements v1 aren’t fully supported yet by the browsers - so we need to install a polyfill:
npm install @webcomponents/custom-elements
Of course, we should import it inside the
Introducing the Package
@angular/elements package has a responsibility of making out of your Angular component a proper custom element, which’s ready to be inserted into the
CustomElementRegistry object. If you’re not familiar with the
CustomElementRegistry object - it’s an object that’s used to register new custom elements and retrieve information about previously registered custom elements.
This package arrives with essential exported classes and functions which make the bridging (attributes, events and life-cycle hooks) between custom elements and Angular to be really easy.
Let’s take a look at the basics:
NgElement- an abstract class which extends the
HTMLElementclass and obligates to implement the necessary life-cycle hooks of a custom element.
createCustomElement- a function which takes a component class and an optional configuration for the created class, such as, setting up the initial injector. This function creates and returns an implementation for
NgElementbased on the provided component.
Here’s the signature of the
Now we’re going to apply these basics on the “Made With Love” component we’ve already built.
Defining a Custom Element
Let’s inspect the component we developed:
Obviously, the piece of code above demonstrates a regular Angular component and there’s nothing special about it (an explanation of that component appears here).
Now, we register that component in the
entryComponents arrays of the
Remember that Custom Elements (Angular Elements in particular) are self-bootstrapping - which means these are automatically started when they are added to the DOM and automatically destroyed when removed from the DOM. Hence, we don’t register a bootstrap component with this module and we don’t need more than an empty and manual invoking of
entryComponents is an array of components which aren’t embedded in a particular template, but still created somehow imperatively.
To produce a custom element of the component we’ve developed - we should invoke
createCustomElement. However, the
registerAsCustomElements function (which we introduced in the Labs project) was renamed to
createCustomElement and unlike the previous one - the fresh function doesn’t insert the created custom element into
CustomElementRegistry - what means we’re responsible to take care of this job (hopefully that will happen automatically in the future).
Let’s use the constructor of
AppModule to define our custom element:
Well, we take the
customElement that was created by
createCustomElement and bind it to a suitable selector. The
define method is what that registers this custom element in
CustomElementRegistry. Notice any registered custom element is accessible by the
customElements array (a read-only property of
The final project so far is attached here:
Using a Custom Element
One of the main objectives of Angular Elements is to allow us to create reusable and embeddable components not just for the Angular community - but for everyone.
We can easily embed the custom element that we just made inside any internal HTML file of the project - assuming that the needed scripts (including polyfills) are imported correctly.
Let’s take the
index.html file as example and use our custom element:
All we do is to attach the line above inside the
Here’s how it seems:
This particular case is pretty straightforward. We embed a custom element inside an Angular application with the appropriate polyfills. In fact, that’s the main objective which the core team has placed for Angular v6.
But, what happens when it comes to embedding the custom element inside an external HTML file, which’s out of our project? 🤔
Officially, Angular v6 doesn’t support standalone publication for Angular Elements so that these aren’t bundled and shippable yet in a way they could be consumed by any type of application. Don’t worry, this is only the first phase of Angular Elements! Absolutely, the best is yet to come, apparently with Angular v7 (when Ivy will land officially).
Ivy is a new backwards-compatible Angular renderer focused on further speed improvements, size reduction, and increased flexibility.
Although that standalone publication isn’t supported yet for Angular Elements, we could definitely take care of this job. It means we might bundle a subset of Angular’s core with our custom element, including polyfills. Of course, this bundle should be published somewhere.
Actually, I’ve created a project that demonstrates this process. In a nutshell, it takes the same component we’re investigating during this article and generates the distribution files using
ng build --prod. These output files are concatenated into a single file using gulp and published into the official registry of npm.
Notice that the concatenated
.js file is about 300KB. That’s rather a lot for a component which just displays a simple message. However, we should also remember that Angular Elements still have a level of dependency on Angular under the hood - along with some polyfills which either increase the bundle size.
Furthermore, there is another issue here regarding version conflicts of Angular. We know that styles in the Shadow DOM might be encapsulated and protected from the parent document - but that’s not the case for scripts. In case we use our custom element in another Angular project (for instance, an Angular v4 application) - it could be problematic to mix it with a different version of Angular (which arrives with the custom element) on top of the same application.
All of these drawbacks will be probably improved when:
- Ivy will land and reduce Angular size dramatically (by bundling what really matters).
- Ivy will either make multiple versions of Angular to be unnecessary (by reducing the level of dependency on Angular’s core in terms of Custom Elements).
- Bundling scripts process will be more automatic with Ivy and Angular CLI in Angular v7.
- The browsers will have a native support for Custom Elements v1 - so these polyfills become unnecessary.
Let’s attempt to load the concatenated scripts and styles here and use the custom element:
As we guess, our custom element is rendered inside a page which isn’t based on Angular at all. 👏
We learned today how to create an embeddable custom element using Angular Elements.
These are important key points to remember:
@angular/elementspackage arrives with a basic exported function for creating Custom Elements from Angular components.
- Polyfills for Custom Elements v1 are still necessary for most of the browsers.
- We should take care of inserting the NgElement into
- Angular Elements of v6 are aimed at using inside Angular applications.
- Angular Elements of v7 will be more standalone and embeddable inside any external applications.
- Embedding Angular Elements of v6 inside external applications is possible but requires a manual and suitable bundling process.
- Ivy will land officially as part of Angular v7.
- Ivy is going to be a key player regarding bundle size and version conflicts of Angular.
Angular core team, thank you very much for this awesome project - I’m sure you made it with ❤️.