Raúl Jiménez
elecash@gmail.com
@elecash
Angular
Elements
about me
Angular GDE
Videogular
Consultancy
Byte
Default
Web
Components
Definition
Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.
Source: webcomponents.org
Specifications
-
Custom Elements
Define new DOM elements
-
HTML Templates and Slots
Define markup fragments
-
Shadow DOM
Styles encapsulation
-
ES Modules
Modular and reusable JS documents
Define an element
class HelloWorld extends HTMLElement {
constructor() {
super();
}
sayHi(name) {
console.log(`hello ${name}`);
}
}
// Define new DOM element
window.customElements.define('hello-world', HelloWorld);
// Create DOM element instance
var helloWorld = document.createElement('hello-world');
// Add DOM element instance to the markup
document.body.appendChild(helloWorld);
// Use custom element
helloWorld.sayHi('Raúl');
You can try this code on the browser console!
Define a Component
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'hello-world',
templateUrl: './hello.component.html',
styleUrls: [ './hello.component.scss' ],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloWorld {
constructor() {
}
sayHi(name: string) {
console.log(`hello ${name}`);
}
}
@Component() decorator is similar to define()
Lifecycle
Similar to Angular and other frameworks too
-
attributeChangedCallback()
Called when an attribute changed
-
connectedCallback()
Called each time the element is added to the markup
-
disconnectedCallback()
Called each time is removed from the markup
Angular Lifecycle
It looks quite similar to Web Components
-
ngOnInit()
Called once when the @Component() is created
-
ngOnChanges()
Called when an @Input() changed
-
ngOnDestroy()
Called when the @Component() is removed
Templates
HTML Templates (we'll see later <slot>)
<table id="producttable">
<thead>
<tr>
<td>UPC_Code</td>
<td>Product_Name</td>
</tr>
</thead>
<tbody>
<!-- Include #productrow dynamically -->
</tbody>
</table>
<template id="productrow">
<tr>
<td class="record"></td>
<td></td>
</tr>
</template>
Source: MDN web docs
Angular Templates
<ngx-toggle [size]="45" [width]="100" [height]="50" (change)="onChange($event)">
<span slot="on" class="text">ON</span>
<span slot="off" class="text">OFF</span>
</ngx-toggle>
Child
<input #cb type="checkbox" id="switch" (click)="onClick($event)">
<label for="switch" [ngStyle]="styles.label">
<span class="dot" [ngStyle]="styles.dot"></span>
<span class="on" [ngStyle]="styles.on">
<slot name="on"></slot>
</span>
<span class="off" [ngStyle]="styles.off">
<slot name="off"></slot>
</span>
</label>
Parent
Custom Elements
Even the Custom Elements API is very familiar
Properties
@Input()
Events
@Output()
Attributes
@HostBinding()
Methods
Public functions
And so on...
Because Angular is actually very close to the spec!
-
Custom Elements
Define new DOM elements
-
HTML Templates (and now Slots too)
Define markup fragments
-
Shadow DOM
Styles encapsulation
-
ES Modules
Modular and reusable JS documents
And so on...
Because Angular is actually very close to the spec!
-
Angular @Component()
Define new Angular components
-
Angular Templates and content projection
Define markup fragments
-
ViewEncapsulation
Styles encapsulation
-
NG Modules
Modular and reusable Angular Modules
So if we're so close...
Why not to compile Angular Components
as Web Components?
Angular
Elements
Definition
Angular elements are Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way.
Source: angular.io
Angular Elements...
In Angular v7!
but before we start...
<ngx-toggle>
A Toggle component, because we're tired of the classic checkbox, right?
@NgModule
Define your entryComponents and schemas
import { BrowserModule } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ToggleComponent } from './toggle/toggle.component';
@NgModule({
declarations: [
AppComponent,
ToggleComponent
],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ],
entryComponents: [ ToggleComponent ],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule {
}
@Component
Create your @Component() as you always did
// Imports...
@Component({
templateUrl: './toggle.component.html',
styleUrls: [ './toggle.component.scss' ],
encapsulation: ViewEncapsulation.ShadowDom,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToggleComponent implements OnInit, OnChanges {
@ViewChild('cb') cb;
@Input() size = 90;
@Input() width = 200;
@Input() height = 100;
@Output() change = new EventEmitter<boolean>();
constructor(private ref: ChangeDetectorRef) {}
// Component logic here...
}
@Component
Don't add any "selector" or you will have conflicts when you define the Web Component
// Imports...
@Component({
selector: 'ngx-toggle',
templateUrl: './toggle.component.html',
styleUrls: [ './toggle.component.scss' ],
encapsulation: ViewEncapsulation.ShadowDom,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToggleComponent implements OnInit, OnChanges {
// Component logic here...
}
@Component
Use ViewEncapsulation.ShadowDom which has been introduced in Angular v7
// Imports...
@Component({
templateUrl: './toggle.component.html',
styleUrls: [ './toggle.component.scss' ],
encapsulation: ViewEncapsulation.ShadowDom,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToggleComponent implements OnInit, OnChanges {
// Component logic here...
}
For the DOM this means using modern Shadow DOM and creating a ShadowRoot for Component's Host Element
Slots
Definition
The HTML <slot> element—part of the Web Components technology suite—is a placeholder inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together.
Source: MDN web docs
Using <slot>
The truth is that Slots are very cool!
<template id="element-details-template">
<span>
<code class="name"><slot name="title"></slot></code>
<p class="desc"><slot name="description"></slot></p>
</span>
<hr>
</template>
<element-details>
<span slot="title">HTML Templates</span>
<span slot="description">A mechanism for holding client-
side content that is not to be rendered when a page is
loaded but may subsequently be instantiated during
runtime using JavaScript.</span>
</element-details>
<script>
// Define Web Component
</script>
Source: MDN web docs
Slots in Angular
You can use Slots in Angular Elements!
In your @Component() template add
<slot name="name-id">
In your parent @Component define your Web Component with name="name-id"
Slots in Angular
Parent @Component
<ngx-toggle (change)="onChange($event)">
<span slot="on" class="fas fa-dollar-sign"></span>
<span slot="off" class="fas fa-euro-sign"></span>
</ngx-toggle>
<input #cb type="checkbox" id="switch" (click)="onClick($event)">
<label for="switch" [ngStyle]="styles.label">
<span class="dot" [ngStyle]="styles.dot"></span>
<span class="on" [ngStyle]="styles.on">
<slot name="on"></slot>
</span>
<span class="off" [ngStyle]="styles.off">
<slot name="off"></slot>
</span>
</label>
Angular Element
Demo
Compiling
Compiling
Enable Ivy Renderer
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"angularCompilerOptions": {
"enableIvy": true
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
Compiling
Fix document-register-element dependency
{
// Other stuff here...
"devDependencies": {
"@angular-devkit/build-angular": "~0.10.0",
"@angular/cli": "~7.0.3",
"@angular/compiler-cli": "~7.0.0",
"@angular/language-service": "~7.0.0",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"document-register-element": "1.8.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
// etc...
}
}
Resources
Web Components
-
Web Components
www.webcomponents.org
-
Polymer Project
www.polymer-project.org
-
Web Fundamentals
developers.google.com
Angular Elements
-
Angular Official Website
angular.io/guide/elements
-
Angular Repository
github.com/angular/angular
-
Today's demo
github.com/Elecash/angular-elements-demo
-
Angular Firebase Youtube
youtube.com/watch?v=4u9_kdkvTsc
Angular Elements
By Raúl Jiménez
Angular Elements
Angular Elements for my talk at GDG DevFest Toulouse
- 1,885