Using Web Components in AngularJS
Cory Rylan
- 5 minutes
AngularJS has been around for quite some time on the Web. Many legacy AngularJS apps need to be migrated or upgraded to the latest Angular or other UI frameworks like Vue or React. There are many challenges when upgrading or migrating an AngularJS application, as well as many different approaches. In this post, we will see how Web Components allow us to create a compatibility bridge between different Web technologies.
Web Components (Custom Elements) are a Web standard for defining custom HTML tags. Web Components work similarly to other component models allowing data to be passed in via Properties/Attributes and notify of changes to the host application through Custom Events.
We won't dig deep into how to build Web Components but instead focus on integrating them into AngularJS. To get an introduction to Web Components, check out this post.
Setting Up
When using Web Components in AngularJS, your application must be on version 1.7.3 or higher. To start, we will set up a basic AngularJS app.
<!doctype html>
<html>
<head></head>
<body>
<app-root></app-root>
</body>
<script src="./index.js"></script>
</html>
import angular from 'angular';
angular.module('app', []);
angular.element(document).ready(() => angular.bootstrap(document, ['app']));
angular.module('app').component('appRoot', {
template: `
hello world
`,
controller: function () { }
});
In our basic AngularJS app, we create a root app module as well as a single Angular component called appRoot
. In this example, we will use a third party Web Component to demonstrate how Web Components integrate with AngularJS. Our Web Component for this example will come from the Clarity Design System, which provides an extensive UI Web Components suite. Web Components can be installed like any other NPM package.
npm install --save @clr/core
Once the package is installed, we can start importing the components we would like to use. The import path or registration step may vary between libraries, so be sure to check the latest documentation.
import angular from 'angular';
import '@clr/core/button/register';
angular.module('app', []);
angular.element(document).ready(() => angular.bootstrap(document, ['app']));
angular.module('app').component('appRoot', {
template: `
<cds-button>hello there</cds-button>
`,
controller: function () { }
});
Here we added a simple button Web Component from the Clarity Design System. If we look at our running app, we will see the following:
Properties and Attributes
Web Components can accept data in a few different ways. Web Components can receive projected content between its element tags and values passed via attributes and properties.
When passing data to a Web Component, its important to understand the differences between attributes and properties. Attributes can be set via HTML and are always string values.
<cds-alert-group status="danger"></cds-alert-group>
However, if we wanted to pass an Object or Array to the component, it would not work as attributes are always strings in HTML. To set complex values, we have to set the property.
const component = document.querySelector('my-custom-element');
component.someProperty = { greeting: 'hello there' };
For Angular, however, we don't need to use document.querySelector
. Instead, we can use the ng-prop
directive. This directive allows us to set the JavaScript property on an element instead of using attributes. Let's add an alert component to display a message and update its status via our Controller properties.
import angular from 'angular';
import '@clr/core/alert/register';
import '@clr/core/button/register';
angular.module('app', []);
angular.element(document).ready(() => angular.bootstrap(document, ['app']));
angular.module('app').component('appRoot', {
template: `
<cds-button status="primary">hello there</cds-button>
<cds-alert-group ng-prop-status="$ctrl.status">
<cds-alert>
General Kenobi. You are a bold one.
</cds-alert>
</cds-alert-group>
`,
controller: function () {
this.status = 'danger';
}
});
In the example above, we can see we use the status
attribute to set the button's status but use ng-prop
to bind the JavaScript property of our controller to the corresponding JavaScript status
property on the alert component. Both are valid, but ng-prop
will work with all value types.
Custom Events
Web Components can be used like any other component and have event listeners. Let's add a click event and show the alert message when the button is clicked.
import angular from 'angular';
import '@clr/core/alert/register';
import '@clr/core/button/register';
import './style.css';
angular.module('app', []);
angular.element(document).ready(() => angular.bootstrap(document, ['app']));
angular.module('app').component('appRoot', {
template: `
<cds-button ng-click="$ctrl.showAlert = true">hello there</cds-button>
<cds-alert-group ng-if="$ctrl.showAlert" ng-prop-status="$ctrl.status">
<cds-alert>
General Kenobi. You are a bold one.
</cds-alert>
</cds-alert-group>
`,
controller: function () {
this.status = 'danger';
this.showAlert = false;
}
});
On our Angular controller, we use a boolean to show the alert when the button is clicked.
The Alert Web Component has a closable feature and can emit an event when the user has clicked the close button. We can use the Custom Event to hide the alert in our Angular template. To listen to Custom Events, we use the ng-on
directive.
import angular from 'angular';
import '@clr/core/alert/register';
import '@clr/core/button/register';
import './style.css';
angular.module('app', []);
angular.element(document).ready(() => angular.bootstrap(document, ['app']));
angular.module('app').component('appRoot', {
template: `
<cds-button ng-click="$ctrl.showAlert = true">hello there</cds-button>
<cds-alert-group ng-if="$ctrl.showAlert" ng-prop-status="$ctrl.status">
<cds-alert ng-on-close_change="$ctrl.showAlert = false" closable>
General Kenobi. You are a bold one.
</cds-alert>
</cds-alert-group>
`,
controller: function () {
this.status = 'danger';
this.showAlert = false;
}
});
With the ng-on
directive, we add the event's name to the end of the directive and an Angular expression on the right-hand assignment. Note since our template is HTML, we lose case-sensitivity; however, Custom Events are case sensitive. Because of this, upper case letters need to have a _
prefix.
The alert component emits a closeChange
custom event so our template would be ng-on-close_change=""
. Now we can listen for the close event on our alert.
With Web Components, we can pass data, listen for events, and share logic between different UI setups. Web Components are a great way to encapsulate UI into a standard component model. To learn more about Web Components, check out a few of these tutorials. The working demo can be found below!