Angular 6 Elements Tutorial by Example: Building Web Components with Angular

In this Angular 6 Elements tutorial by example, we'll learn how to build web components or custom elements with Angular 6 and use them everywhere using the latest version of Angular and Angular CLI 6 — the version 6 released on 2018–04–03.

The Angular 6 Elements package @angular/elements allows you to create native custom elements and author web components with Angular.

The @angular/elements package provides a createCustomElement() API that can be used to transform Angular Components to native Custom Elements.

What Is a Web Component or Custom Element?

A Custom Element provides a way to create web components i.e new DOM elements that behave like standard HTML elements

With Custom Elements, web developers can create new HTML tags, beef-up existing HTML tags, or extend the components other developers have authored. The API is the foundation of web components. It brings a web standards-based way to create reusable components using nothing more than vanilla JS/HTML/CSS. The result is less code, modular code, and more reuse in our apps. https://developers.google.com/web/fundamentals/web-components/customelements


class AppDrawer extends HTMLElement {

  constructor() {   
      super() 
    }
    ...
}

window.customElements.define('app-drawer', AppDrawer);

You can use it just like any standard HTML tag:

<app-drawer></app-drawer>

Installing the Angular 6 CLI

Head to your terminal and run the following command to install the Angular CLI 6 globally:

npm i -g @angular/cli

This will install the latest version and you may need to add sudo depending on your npm configuration.

Creating a New Angular 6 Example Project

Head back to your terminal and run the following command to create a new Angular 6 project using the Angular CLI 6

ng new angular-webcomponents-demo --prefix custom

We simply use the usual ng new command and you can use all the other parameters to add routing or sass etc. But also note the custom prefix -- This is required for custom elements

Adding the Angular Elements 6 Package

To be able to create web components we need to install the Angular Elements 6 package. You can simply use the ng add command to install the package:

ng add @angular/elements

This command adds the document-register-element.js polyfill required for web browsers that don't support custom elements yet and @angular/elements package.

Creating an Example Angular 6 Component

An Angular component is an angular concept that's different from a web component so let's see how we can create a component and transform it to web component using the createCustomElement() function of @angular/elements.

First let's generate a new Angular component using the Angular CLI 6:

ng g component analytics-counter --inline-style --inline-template -v Native

This command creates a new analytics-counter folder and generates the following files and updates app.module.ts

  • analytics-counter.component.html
  • analytics-counter.component.css
  • analytics-counter.component.ts
  • analytics-counter.component.spec.ts

Using ViewEncapsulation.Native encapsulation results in styles, template and code bundled into the same file.

Adding the Component to Main NgModule

We need to follow some steps in order to transform the Angular component to a web component. Open app.module.ts then add the following changes:

First import Injector from the @angular/core package and createCustomElement from the @angular/elements package:

import  { Injector} from '@angular/core';
import  { createCustomElement } from '@angular/elements';

Next, we need to instruct Angular 6 to compile it the component by adding it to the entryComponents array of the main module:

...
entryComponents :  [
   AnalyticsCounterComponent
]

Now you are ready to transform the component into a web component by calling the createCustomElement() method from the ngDoBootstrap() method:

export class AppModule {
  constructor(private injector : Injector){}
  ngDoBootstrap(){
      const el = createCustomElement(AnalyticsCounterComponent, {injector : this.injector});
  customElements.define('analytics-counter',el);
  }
  }

We are bootstrapping our Angular Component as a custom element in the ngDoBootstrap() method.

The createCustomElement method takes two parameters:

  • The first parameter is the Angular component that will be used to create the custom element.
  • The second parameter is a configuration object that has an injector property set to the current Injector instance.
  • Using the createCustomElement API we can transform and Angular 6 component to a Custom Element

We defined a custom element using the standard browser API customElements.define()

_createCustomElement_ Builds a class that encapsulates the functionality of the provided component and uses the configuration information to provide more context to the class. Takes the component factory’s inputs and outputs to convert them to the proper custom element API and add hooks to input changes.

This is the content of the app.module.ts file:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AnalyticsCounterComponent } from './analyticscounter/analyticscounter.component';

@NgModule({
declarations: [AnalyticsCounterComponent],
imports: [BrowserModule],
entryComponents: [AnalyticsCounterComponent]
})
export class AppModule {

constructor(private injector: Injector) {
    const analyticsCounter = createCustomElement(AnalyticsCounterComponent, { injector });
    customElements.define('analytics-counter', analyticsCounter);
}
ngDoBootstrap() {}
} 

Implementing the Component Logic

For the sake of brevity, we are not going to add any special logic to the component.

Building the Project for Production

Just like any normal Angular project. You can use the following command to generate a production build

ng build --prod --output-hashing none

In the dist folder, you'll find multiple generated files. Ideally, we must have only one file for using the web component but unfortunately the Angular CLI doesn't provide a way to produce on file for now.

We can simply create a build script to produce only one JS file from the multiple files generated by the Angular CLI.

Create a concatenate.js file in the root folder of your project then add the following content:

const fs = require('fs-extra');
const concat = require('concat');
(async function build() {
const files = [
'./dist/<PROJECT_NAME>/runtime.js',
'./dist/<PROJECT_NAME>/polyfills.js',
'./dist/<PROJECT_NAME>/scripts.js',
'./dist/<PROJECT_NAME>/main.js',
]
await fs.ensureDir('elements')
await concat(files, 'elements/analytics-counter.js');
await fs.copyFile('./dist/angular-elements/styles.css', 'elements/styles.css')
await fs.copy('./dist/angular-elements/assets/', 'elements/assets/' )
})()
  • Creates a new sub-folder elements inside the project folder
  • The JS files runtime.js, polyfills.js, scripts.js and main.js are concatenated into a new file analytics-counter.js inside the elements folder
  • styles.css from the production build is copied to the elements folder
  • Files from the assets folder are copied to the elements folder

You need to install fs-extra and concat from npm using:

npm install fs-extra concat --save-dev

Now let's add a script to package.json:

{
"scripts": {
...
"build:elements": "ng build --prod --output-hashing none && node concatenate.js"
},
}
}

You can run the script using:

npm run build:elements

Example Using our Custom Element

Now that we have one file that holds our custom element. Let's see how we can use our web component in other file.

Create an index.html file then add the following content:

<!doctype html>
<html lang="en">
<head>
<title>Angular Elements Demo</title>
</head>
<body>
<analytics-counter></analytics-counter>
<script src="analytics-counter.js"></script>
</body>
</html>

We need a way to serve this file so we'll use a simple HTTP server -- http-server :

npm install http-server -g

Conclusion

Throughout this tutorial by example, we've seen how to use Angular 6 Elements to build standard web components from Angular 6 components.


comments powered by Disqus