We can now begin to develop our regular Angular component. First, we define our template with the `./src/app/hideaway-message/hideaway-message.component.html` file (although this could be done in the component main file).
<div class="panel panel-default">
<div class="panel-heading">
{{title}}
<button (click)="hideContent($event)" style="float: right">
{{ btnText }}
</button>
</div>
<div class="panel-body" *ngIf="!hidden">
{{content}}
</div>
</div>
We also add our styling in our `./hideaway-message.component.css` within the same folder as the template file:
.panel {
margin: 10px;
background-color: #fff;
border: 1px solid transparent;
border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,.05);
box-shadow: 0 1px 1px rgba(0,0,0,.05);
}
.panel-default {
border-color: #ddd;
}
.panel-default>.panel-heading {
color: #333;
background-color: #f5f5f5;
border-color: #ddd;
}
.panel-heading {
padding: 10px 15px;
border-bottom: 1px solid transparent;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.panel-body {
padding: 15px;
}
We can now define our component in the `hideaway-message.component.ts` file like this:
import { Component, Input, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'hideaway-message',
templateUrl: './hideaway-message.component.html',
styleUrls: ['./hideaway-message.component.css',],
encapsulation: ViewEncapsulation.Native
})
export class HideawayMessageComponent {
constructor() { }
hidden = false;
btnText = 'Hide'
hideContent(e) {
e.preventDefault();
this.hidden = !this.hidden;
this.btnText = this.btnText == 'Hide' ? 'Show' : 'Hide';
}
@Input() content;
@Input() title;
}
ViewEncapsulation helps compile the CSS down to JavaScript (i.e., using Shadow DOM), so the component can be used without having to worry about the CSS referencing properly. We can now check to see if our component is working by putting it within the app-root tag in the `index.html` file:
<body>
<app-root>
<hideaway-message title="Lorem ipsum dolor sit amet"
content="Lorem ipsum dolor sit amet, iusto appellantur vix te, nam affert feugait menandri eu. Magna simul ad est. Nostrum neglegentur ius at, at pertinax repudiare vel. Vim an adolescens quaerendum.">
</hideaway-message>
</app-root>
</body>
We now start our application so it can be in a browser (on the default Angular port http://localhost:4200/) using the command below in the command line:
ng serve
Now that our component is fully functional, the next step is to convert it into an Angular element. We do this by editing the `app.module.ts` file to look like this:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { HideawayMessageComponent } from './hideaway-message/hideaway-message.component';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
HideawayMessageComponent,
AppComponent
],
imports: [
BrowserModule
],
entryComponents: [
HideawayMessageComponent
]
})
export class AppModule {
constructor(private injector: Injector){
}
ngDoBootstrap(){
const element = createCustomElement(
HideawayMessageComponent,
{ injector: this.injector }
);
customElements.define('hideaway-message', element)
}
}
In the code above, we have stopped Angular from automatically bootstrapping the application. We accomplished that by removing the bootstrap property in the decorator parameter object and overwriting the `ngDoBootstrap` method in the `AppModule` class. We also add the component, `HidawayMassageComponent` to the entryComponents array, to instruct Angular to create the component, even though it is not part of a template.
Then within `ngDoBootstrap` method, the `HidawayMassageComponent` is parsed using the function `createCustomElement` from the Angular element module to prepare it to be added to `CustomElementRegistry` (this process includes making the component extend the HTMLElement abstract class). After the component has been transformed into an element, we then add it to the list of elements available to the DOM using `customElement` object which is available globally in JavaScript. Now we can use the element in an HTML file within our project. For example, the `index.html` file could be rewritten as below:
<body>
<div style="width: 90%; margin: auto;">
<hideaway-message
title="Lorem ipsum dolor sit amet"
content="Lorem ipsum dolor sit amet, iusto appellantur vix te.">
</hideaway-message>
<hideaway-message
title="Lorem ipsum dolor sit amet"
content="Lorem ipsum dolor sit amet, iusto appellantur vix te.">
</hideaway-message>
<hideaway-message
title="Lorem ipsum dolor sit amet"
content="Lorem ipsum dolor sit amet, iusto appellantur vix te.">
</hideaway-message>
<hideaway-message
title="Lorem ipsum dolor sit amet"
content="Lorem ipsum dolor sit amet, iusto appellantur vix te.">
</hideaway-message>
</div>
</body>
Building the Custom Element
Now that we can use the element in an HTML document within our project, the next step is to try to make sure that we can use the element by referencing a file with the element declaration and dependencies. For this, we need to pull in a module called `concat`, using npm:
npm install concat --save-dev
Next, we create a build script in the root of the project folder and name it accordingly. In the `build-script.js` file, we add the path to the production build files and a path to the output file after concatenation, and pass it to the concat function like below:
const concat = require('concat');
const files = [
'./dist/AngularElements/runtime.js',
'./dist/AngularElements/polyfills.js',
'./dist/AngularElements/main.js'
];
const outputFile = './dist/final-bundle.js';
concat(files, outputFile);
This list of files is the product of the Angular build command, and these files are usually found in the dist directory in the project root directory – please replace `AngularElements` in the paths above with the name of your project. Please note that these paths are specific to Angular apps built with version 6.0 of the CLI or higher.
We need to add a script in the `package.json` file to firstly build our application in prod mode, without hashing the output file, and run our build-script. We add the code below to the scripts
object:
"build-element": "ng build --prod --output-hashing=none && node build-script.js"
Code Formatting using CodeMix
Now we can run the command below to get our concatenated file:
npm run build-element