Angular Custom Elements Overview
Published June 30, 2024 by T&S Software Admin
Angular custom elements are a powerful feature that allows developers to package Angular components as custom elements, also known as Web Components. This web standard enables defining new HTML elements in a framework-agnostic way, making it a versatile tool for web development.
What Are Custom Elements?
Custom elements are a Web Platform feature available on all browsers supported by Angular. They extend HTML by allowing you to define tags whose content is created and controlled by JavaScript code. The browser maintains a CustomElementRegistry
of defined custom elements, mapping an instantiable JavaScript class to an HTML tag.
How Angular Integrates Custom Elements
The @angular/elements
package exports a createCustomElement()
API, which bridges Angular's component interface and change detection functionality to the built-in DOM API. This transformation makes all the required Angular infrastructure available to the browser, making the integration seamless and efficient.
Using Custom Elements
Custom elements bootstrap themselves when they are added to the DOM and are destroyed when removed from the DOM. Once a custom element is added to the DOM, it looks and behaves like any other HTML element and does not require special knowledge of Angular terms or usage conventions.
To add the @angular/elements
package to your workspace, run the following command:
npm install @angular/elements --save
How It Works
The createCustomElement()
function converts a component into a class that can be registered with the browser as a custom element. After registering your configured class with the browser's custom-element registry, you can use the new element just like a built-in HTML element in content that you add directly to the DOM:
<my-popup message="Use Angular!"></my-popup>
Transforming Components to Custom Elements
Angular provides the createCustomElement()
function for converting an Angular component, along with its dependencies, into a custom element. The conversion process implements the NgElementConstructor
interface and creates a constructor class configured to produce a self-bootstrapping instance of your component.
Use the browser's native customElements.define()
function to register the configured constructor and its associated custom-element tag with the browser's CustomElementRegistry
. When the browser encounters the tag for the registered element, it uses the constructor to create a custom-element instance.
Mapping Component Properties to HTML Attributes
A custom element hosts an Angular component, providing a bridge between the data and logic defined in the component and standard DOM APIs. Component properties and logic map directly into HTML attributes and the browser's event system. The creation API parses the component for input properties and defines corresponding attributes for the custom element. For example:
- For a component with
@Input('myInputProp') inputProp
, the corresponding custom element defines an attributemy-input-prop
. - Component outputs are dispatched as HTML Custom Events, with the event name matching the output name. For example,
@Output() valueChanged = new EventEmitter()
corresponds to dispatch events with the name "valueChanged".
Example: A Popup Service
To illustrate the usage of custom elements, let's consider a Popup Service example. This example defines a component that can either be loaded dynamically or converted to a custom element.
Popup Component
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({
standalone: true,
selector: 'my-popup',
template: `
<span>Popup: {{ message }}</span>
<button type="button" (click)="closed.next()">✖</button>
`,
animations: [
trigger('state', [
state('opened', style({ transform: 'translateY(0%)' })),
state('void, closed', style({ transform: 'translateY(100%)', opacity: 0 })),
transition('* => *', animate('100ms ease-in')),
]),
],
styles: [
`
:host {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #009cff;
height: 48px;
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid black;
font-size: 24px;
}
button {
border-radius: 50%;
}
`,
],
})
export class PopupComponent {
@HostBinding('@state')
state: 'opened' | 'closed' = 'closed';
@Input()
get message(): string {
return this._message;
}
set message(message: string) {
this._message = message;
this.state = 'opened';
}
private _message = '';
@Output()
closed = new EventEmitter<void>();
}
Popup Service
The Popup Service creates an injectable service that provides two different ways to invoke the PopupComponent
: as a dynamic component or as a custom element.
import { Injectable, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { PopupComponent } from './popup.component';
@Injectable({
providedIn: 'root'
})
export class PopupService {
constructor(private injector: Injector) {
const PopupElement = createCustomElement(PopupComponent, { injector });
customElements.define('popup-element', PopupElement);
}
// Method to dynamically load the component
showAsComponent(message: string) {
// Logic to dynamically load the component
}
// Method to use the custom element
showAsElement(message: string) {
const popupEl = document.createElement('popup-element') as any;
popupEl.message = message;
document.body.appendChild(popupEl);
}
}
App Component
The application's root component uses the PopupService
to add the popup to the DOM at runtime. When the application runs, the root component's constructor converts PopupComponent
to a custom element.
import { Component } from '@angular/core';
import { PopupService } from './popup.service';
@Component({
selector: 'app-root',
template: `
<button (click)="showPopup()">Show Popup</button>
`,
})
export class AppComponent {
constructor(private popupService: PopupService) {}
showPopup() {
this.popupService.showAsElement('Hello, this is a custom element!');
}
}
Typings for Custom Elements
Custom elements created with Angular extend NgElement
(which in turn extends HTMLElement
). They will have a property for each input of the corresponding component. For accurate typings, you can cast the return value of the relevant DOM methods to the correct type.
Pros and Cons of Using Angular Custom Elements
Pros
- Framework Agnostic: Custom elements work across different frameworks.
- Reusable Components: Enhance component reusability in various projects.
- Ease of Integration: Seamlessly integrates with existing projects.
Cons
- Performance Overhead: Potential for increased load time due to additional JavaScript.
- Complexity: Additional setup complexity compared to standard Angular components.
FAQs
What are Angular custom elements?
- Angular custom elements are Angular components packaged as custom elements (Web Components) to create reusable HTML elements.
How do you create a custom element in Angular?
- Use the
createCustomElement()
function from the@angular/elements
package to convert an Angular component into a custom element.
What browsers support custom elements?
- Custom elements are supported on all browsers that Angular supports.
Can custom elements be used outside of Angular applications?
- Yes, custom elements can be used in any web application, regardless of the framework.
What is the CustomElementRegistry
?
- It is a browser-maintained registry mapping custom element tags to their JavaScript classes.
How do custom elements handle input properties?
- Input properties are mapped to corresponding HTML attributes, using dash-separated lowercase attribute names.
Can custom elements dispatch events?
- Yes, custom elements dispatch HTML Custom Events corresponding to the component's outputs.
Are there any naming conventions for custom element tags?
- Avoid using the component's selector as the custom element tag name to prevent unexpected behavior.
How are custom elements added to the DOM?
- Custom elements bootstrap themselves when added to the DOM and are destroyed when removed.
What are the benefits of using Angular custom elements?
- They enhance component reusability, work across different frameworks, and integrate seamlessly with existing projects.
By leveraging Angular custom elements, developers can create reusable, framework-agnostic web components that simplify the integration and maintenance of complex web applications. With robust tooling and broad browser support, Angular custom elements are a powerful addition to any developer's toolkit.