This website uses cookies to ensure you get the best experience. Learn More.
Liferay Remote Application with Angular Custom Elements
Angular Clock
Liferay DXP has traditionally offered a wide variety of extension mechanisms to extend and customize the platform. Most of these mechanisms require the deployment of custom-written code to be activated.
As we move towards SaaS solutions and Cloud offerings, we need to provide other extension mechanisms that don't require customers to deliver and run arbitrary code on the company servers.
In this blog we will be tackling HTML Custom Components which have been built with angular as a new extension mechanism.
As a starting point we need to create a normal angular application, which will be converting it into a custom html component.
Please follow the below instructions to create the application:
Install Angular CLI npm install -g @angular/cli
Create new angular project ng new ng-clock
Install the required node modules npm install
Hint
This example has been developed using the following framework / tools versions
Liferay 7.4
NPM 8.1.2
Node 16.13.1
Angular Cli 12.2.4
In order to sense the output of our example, let's add a custom behavior to the application, in this example we will make it very simple, we will assume that we are building a clock.
Replace the code inside app.component.ts with the following code:
import { AfterViewInit, Component } from '@angular/core';
@Component({
selector: 'ng-clock',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AngularClock implements AfterViewInit {
timerId: any;
ngAfterViewInit() {
this.generateDialLines();
this.timerId = this.getTime();
}
getTime() {
setInterval(()=>{
this.clock();
}, 100);
clock() {
var weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
d = new Date(),
h = d.getHours(),
m = d.getMinutes(),
s = d.getSeconds(),
date = d.getDate(),
month = d.getMonth() + 1,
year = d.getFullYear(),
hDeg = h * 30 + m * (360/720),
mDeg = m * 6 + s * (360/3600),
sDeg = s * 6;
var day = weekday[d.getDay()];
var monthtext = month+"";
if(month < 9) {
monthtext = "0" + month;
this.h_transform = "rotate("+hDeg+"deg)";
this.m_transform = "rotate("+mDeg+"deg)";
this.s_transform = "rotate("+sDeg+"deg)";
this.date_text = date+"/"+monthtext+"/"+year;
this.day_text = day;
generateDialLines()
{
for (var i = 1; i < 60; i++) {
var temp = "rotate(" + 6 * i + "deg)";
this.diallines.push(temp);
public h_transform:any;
public m_transform:any;
public s_transform:any;
public date_text :any;
public day_text : any;
public diallines = new Array();
Replace the code inside app.component.html with the following code:
<div class="clock">
<div>
<div class="info date">{{date_text}}</div>
<div class="info day">{{day_text}}</div>
</div>
<div class="dot"></div>
<div class="hour-hand" style="transform:{{h_transform}};"></div>
<div class="minute-hand" style="transform:{{m_transform}};"></div>
<div class="second-hand" style="transform:{{s_transform}};"></div>
<span class="h3">3</span>
<span class="h6">6</span>
<span class="h9">9</span>
<span class="h12">12</span>
<div class="diallines"></div>
<div class="diallines" *ngFor="let line of diallines" style="transform: {{line}};"></div>
Replace the code inside app.component.css with the following code:
.clock {
background: #ececec;
width: 300px;
height: 300px;
margin: auto;
border-radius: 50%;
position: relative;
box-shadow: 0 2vw 4vw -1vw rgba(0, 0, 0, 0.8);
.dot {
width: 14px;
height: 14px;
background: #ccc;
top: 0;
left: 0;
right: 0;
bottom: 0;
position: absolute;
z-index: 10;
box-shadow: 0 2px 4px -1px black;
.hour-hand {
z-index: 5;
width: 4px;
height: 65px;
background: #333;
top: 79px;
transform-origin: 50% 72px;
left: 50%;
margin-left: -2px;
border-top-left-radius: 50%;
border-top-right-radius: 50%;
.minute-hand {
z-index: 6;
height: 100px;
background: #666;
top: 46px;
transform-origin: 50% 105px;
.second-hand {
z-index: 7;
width: 2px;
height: 120px;
background: gold;
top: 26px;
lefT: 50%;
margin-left: -1px;
transform-origin: 50% 125px;
span {
display: inline-block;
color: #333;
font-size: 22px;
font-family: 'Poiret One';
font-weight: 700;
z-index: 4;
.h12 {
top: 30px;
margin-left: -9px;
.h3 {
top: 140px;
right: 30px;
.h6 {
bottom: 30px;
margin-left: -5px;
.h9 {
left: 32px;
.diallines {
z-index: 2;
height: 15px;
transform-origin: 50% 150px;
.diallines:nth-of-type(5n) {
height: 25px;
.info {
width: 120px;
height: 20px;
border-radius: 7px;
text-align: center;
line-height: 20px;
color: #000;
font-size: 11px;
top: 200px;
margin-left: -60px;
font-family: "Poiret One";
z-index: 3;
letter-spacing: 3px;
.date {
top: 80px;
.day {
Replace the code inside app.module.ts with the following code:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AngularClock } from './app.component';
import { APP_BASE_HREF } from '@angular/common';
@NgModule({
declarations: [
AngularClock
],
imports: [
BrowserModule,
AppRoutingModule
providers: [{provide: APP_BASE_HREF, useValue: "/"}],
bootstrap: [AngularClock]
export class AppModule { }
Replace the code inside <app-root></app-root> with <angualr-clock></angualr-clock> inside index.html file.
Test the application in the browser, by executing the following command: Ng serve
Navigate to http://localhost:4200 to check the result application running, and you should see the following output.
Now we have designed and implemented an angular analog clock, what we have done till now is a pure standard Angular implementation.
Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.
Angular elements are Angular components packaged as custom elements (also called Web Components), a web standard for defining new HTML elements in a framework-agnostic way.
In order to build angular as a custom web component, we will need to follow the below steps, please note that the following steps are available in details at https://angular.io/guide/elements.
Install @angular/elements by executing the following: ng add @angular/elements
Define the component(s) which we want to build as a custom element (web component); for that replace the code in app.module.ts with the following:
import { Injector, NgModule } from '@angular/core';
import { createCustomElement } from "@angular/elements";
declarations: [AngularClock],
imports: [BrowserModule,AppRoutingModule],
entryComponents: [AngularClock],
bootstrap : [AngularClock]
export class AppModule {
constructor(private injector: Injector) {
const appElement = createCustomElement(AngularClock, {
injector: this.injector
});
customElements.define("ng-clock", appElement);
If you look at the following line, you will notice that we have defined our html tag name, so you will be able to use your component by embedding <ng-clock></ng-clock>
Open angular.json and replace "outputHashing": "all" with "outputHashing": "none"
Create the script to build the custom element, create a file at your project root level and name it “build-custom-element.js”
Place the following code in “build-custom-element.js”
const fs = require('fs-extra');
const concat = require('concat');
(async function build() {
const files = [
'./dist/ng-clock/runtime.js',
'./dist/ng-clock/polyfills.js',
'./dist/ng-clock/main.js'
];
await fs.ensureDir('angular-elements-build');
await concat(files, 'angular-elements-build/angular-elements.js');
await fs.copy('./dist/ng-clock/styles.css', 'angular-elements-build/styles.css');
//uncomment the below line if you have assets folder in your project
//await fs.copy('./dist/ng-clock/assets/', 'angular-elements/assets/');
})();
Install the required NPM modules npm i concat fs-extra --save-dev
In your package.json file, add the following line under scripts "build:ng-clock": "ng build ng-clock --prod && node build-custom-element.js"
Execute the following command to build your custom element npm run build:ng-clock
Congratulations! You have implemented and built your angular web component, this component can be used anywhere by adding the generated javascript file and css, this will allow you to use the custom component in any plain html page or any other places like Liferay.
Liferay 7.4 has been empowered with a new extension mechanism by adding custom web component using Liferay Remote Applications, please follow the below instructions in order to add the Angular Clock Component we have just created:
Navigate to Liferay -> Content and Data -> Documents and Media and create a folder to host our web component files.
Copy the files in your angular project folder -> angular-elements-build
Angular-elements.js
styles.css
Make sure to upload the above mentioned files in the Liferay folder you have just created.
Navigate to Liferay -> Global Menu -> Applications -> Custom Apps -> Remote Apps
Create New Remote App
In the type filed, select “Custom Element”
In the HTML Element Name type “ng-clock”
In the URL enter the Angular-elements.js full url from liferay file information panel.
In the CSS URL enter the styles.css full url from liferay file information panel.
Save the application
Congratulations! You can now use your NG-Clock in any liferay page by drag and drop it from the page editor tool box under Widgets -> Remote Apps.
Angular Application Sample Code “NG Clock” has been published on github:
https://github.com/mahmoudhtayem87/ng-clock