Creating Framework Agnostic Apps with Angular Elements

Browsers are becoming more and more powerful. Browsers are now able to do what was previously only possible with native apps, and web apps that take advantage of the modern browser capabilities go under the name Progressive Web Apps. This includes a browser specification that enables browsers to have functionality such as offline availability, hardware support, and Bluetooth. Also, new browser specifications are enabling what was previously only available for SPA frameworks to the browser natively using web components that are encapsulating functionality and styling with techniques such as shadow dom and creating web components using custom-elements.

What is custom-elements? Custom-elements is part of the web component standard and is custom HTML tags that encapsulate functionality and styling. It is the browser-native implementation of what Angular is doing with javascript rendered components, just running natively and framework agnostic in the browser!

Angular has created a library for converting components into custom elements. This enables us to write components using the Angular framework and run the component in non-Angular applications. That way you can mix different front-end frameworks, as it all gets compiled to custom-elements.

Let’s see how easy it is to convert an Angular component to a custom element.

Converting an Angular component to a custom-element using @angular/elements

We are gonna create a simple demo of an Angular app by creating a component and using it to create a custom-element like in my Github demo.

The first step is to create a new Angular CLI project:

ng new angular-elements-demo

We need to install Angular elements:

npm i --save @angular/elements

And install some polyfills for web component browser compatibility:

npm i --save @webcomponents/custom-elements

And add the following lines to polyfills.ts:

import '@webcomponents/custom-elements/src/native-shim';

import '@webcomponents/custom-elements/src/custom-elements';

Hereafter you create a new component, that will later become a custom-element.

ng g c hello

Make sure to set ViewEncapsulation to native since we are to be working with custom-elements.

In the HelloComponent's decorator:

encapsulation: ViewEncapsulation.Native

For making Angular elements outside of Angular you need to change a couple of things in the AppModule:

  1. The HelloComponent is the component we want to create a custom element from. To do this we need to add it to declarations and entryComponents because the component is being created programatically (not created in a template).
  2. We want to use our custom element as our bootstrapped component, so to do this we need to setup the ngDoBootstrap lifecycle hook. Also we need to ensure that AppComponent is removed from the declarations and bootstrap properties in the decorator of AppModule.
  3. In the ngDoBootstrap create a new custom-element using createCustomElement. Make sure to provide the injector.
  4. Define the custom element using define. Here you specify the custom-elements HTML tag and the custom element to create.

Now when the app runs it will run a custom-element. You can remove everything from index.html and only specify the custom element as:

<hello></hello>

And you should see the HelloComponent being rendered!

The benefit of this is that this custom-element can be rendered in any framework now, as long as the compiled javascript bundles are provided. It is possible to run this app inside a React app and also there might be some performance benefits because of how browsers optimize for running web components vs running a javascript framework.

Publication of custom-elements

Unfortunately the way to use a custom elements created with Angular Elements is to build the bundles with ng build --prod --output-hashing=none and concat all the js files into one. Currently this creates a pretty big bundle size, especially for an App that just displays some text. For this reason, it might not be reasonable to use Angular Elements in production yet.

Angular v7 and the Ivy renderer should make the published bundle of custom-elements created with Angular Elements even smaller, making this more useful in production. Also as the custom-element standard gets implemented in more browsers polyfills might not be needed anymore.

Conclusion

We discussed the benefits of creating framework agnostic apps using Angular Elements and saw how easy it is to convert an Angular component to a custom-element. Still, the technology has some drawbacks in a big bundle size and the need for polyfills to ensure browser support for custom-elements.


Originally published at Christian Lüdemann IT.