Current Angular version doesn’t provide an option to export component as single local file which can used in any non angular application. However it can be achieved by making changes in building and deployment steps. In my example I have created two angular elements a button and alert message. Both components are compiled and exported as single local file which I’m loading in a plain html file with javascript.
Here are the steps follows:
1. Add ButtonComponent and AlertComponent in entryComponent list. In ngDoBootstrap and define them as custom elements.
This is how my app.module looks:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AppComponent } from './app.component';
import { ButtonComponent } from './button/button.component';
import { AlertComponent } from './alert/alert.component';
@NgModule({
declarations: [AppComponent, ButtonComponent, AlertComponent],
imports: [BrowserModule],
entryComponents: [ButtonComponent, AlertComponent]
})
export class AppModule {
constructor(private injector: Injector) {
}
ngDoBootstrap() {
const customButton = createCustomElement(ButtonComponent, { injector: this.injector });
customElements.define('my-button', customButton);
const alertElement = createCustomElement(AlertComponent, { injector: this.injector});
customElements.define('my-alert', alertElement);
}
}
- Here is my button component:
import {
Input,
Component,
ViewEncapsulation,
EventEmitter,
Output
} from '@angular/core';
@Component({
selector: 'custom-button',
template: `<button (click)="handleClick()">{{label}}</button>`,
styles: [
`
button {
border: solid 3px;
padding: 8px 10px;
background: #bada55;
font-size: 20px;
}
`
],
encapsulation: ViewEncapsulation.Native
})
export class ButtonComponent {
@Input() label = 'default label';
@Output() action = new EventEmitter<number>();
private clicksCt = 0;
handleClick() {
this.clicksCt++;
this.action.emit(this.clicksCt);
}
}
- Here is my alert component:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'alert-message',
template: '<div>Alert Message: {{message}}</div>',
styles: [
`
div {
border: 1px solid #885800;
background-color: #ffcd3f;
padding: 10px;
color: red;
margin:10px;
font-family: Arial;
}
`]
})
export class AlertComponent {
@Input () message: string;
}
- Build configurations in angular.json:
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": [
{
"input":
"node_modules/document-register-element/build/document-register-element.js"
}
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "angular6-elements:build"
},
"configurations": {
"production": {
"browserTarget": "angular6-elements:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular6-elements:build"
}
}
- After build, I concatenate
runtime, polyfills, script
js files into single script file and export elements.js
which contains the custom elements
(optional: gzip those files)
serve it using http-server deploy --gzip
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod --output-hashing=none",
"package": "npm run package-base && npm run package-elements",
"package-base": "cat dist/{runtime,polyfills,scripts}.js | gzip > deploy/script.js.gz",
"package-elements": "cat dist/main.js | gzip > deploy/elements.js.gz",
"serve": "http-server deploy --gzip",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}
- Finally I include
script.js
and elements.js
in index.html (in deploy directory) to tell the browser about the custom elements.
Now my-button and my-alert can be included in index.html
. In this example, the button is shown on-load and Alert message is added dynamically (with counter number) on click of the button.
Here is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Button Test Page</title>
<script src="script.js"></script>
<script src="elements.js"></script>
</head>
<body>
<my-button label="Show Alert Message!"></my-button>
<p></p>
<div id="message-container"></div>
<script>
const button = document.querySelector('my-button');
const msgContainer = document.querySelector('#message-container');
button.addEventListener('action', (event) => {
console.log(`"action" emitted: ${event.detail}`);
button.setAttribute("label", "Show Next Alert Message!");
msgContainer.innerHTML += `<my-alert message="Here is a message #${event.detail} created dynamically using ng elements!!!"></my-alert>`;
});
</script>
</body>
</html>
Here is my link for my git repo
Hope this will help!
Thanks.