[Tutorial] How to create a widget using custom elements in Angular?
We know that as a developer, reusing code is a very essential part of programming. This results in the reusability and modularisation of our code. Angular aims to achieve both things with the creation of custom elements.
Angular Custom Elements are encapsulated to have their own functionality which can be reused in any web app. HTML version 5 comes with custom elements, which provide a way to define and register their own and fully-featured DOM elements.
Custom Elements allows the developers to create their own new custom HTML tags which will perform different functions/functionality. In other words, we can create an Angular Custom Element, that can be used on any webpage, even if that webpage does not use Angular at all. This way, we can have Angular components displayed on a page that uses React, Vue, Angular JS, or even some HTML generated with JSP, PHP, ASP, etc.
Angular Custom Elements can be a great way to get started with an application without having to migrate an entire application to another platform.
Steps for creating an Angular Custom Element :
We have created a custom element for embedding a widget into our product BlockSurvey. The steps for creating a widget has been given below.
Create a new component in angular project which has to be used as a custom element.
- Start with Angular CLI to add the @angular/elements package to your project for creating a custom element:
ng add @angular/elements
- Next in the app.module.ts, we ask angular to create a custom element using ngDoBootstrap method. This method will directly load the custom element component instead of app component.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { BlocksurveyWidgetComponent } from './blocksurvey-widget/conv-widget.component';
import { APP_BASE_HREF } from '@angular/common';@NgModule({ …
providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
bootstrap: []
})export class AppModule {
constructor(private injector: Injector) { }
ngDoBootstrap() {
const el = createCustomElement(BlocksurveyWidgetComponent, {
injector: this.injector
});
customElements.define('blocksurvey-widget', el);
}}
- Now we have to remove ngZone from the Blocksurveywidget-component.ts so that dual ngZone are not loaded while using the custom element in a different project.
import { enableProdMode, NgZone } from '@angular/core';
platformBrowserDynamic().bootstrapModule(AppModule ,{
ngZone: 'noop'
}).catch(err => console.error(err));
- Without ngZone we cannot detect the changes or any function being used in the component. So no functions will be working. In order to make it work, we have to use ChangeDetectorRef.
import { Component, OnInit, ViewEncapsulation, Input, ChangeDetectorRef,SimpleChanges, OnChanges } from '@angular/core';
@Component({ …}) constructor(private ref: ChangeDetectorRef, private sanitizer: DomSanitizer) { } toggleConversationalView() {
// Toggle the value
this.showConv = !this.showConv;
// Trigger the change detection
this.ref.detectChanges();
}
- Now in order to style the component, we need to use ViewEncapsulation in Blocksurveywidget-component.ts which has four different values: Native , Emulated , ShadowDom , None.
import { Component, OnInit, ViewEncapsulation, Input, ChangeDetectorRef,SimpleChanges, OnChanges } from '@angular/core';@Component({
selector: 'conv-widget',
templateUrl: './blocksurvey-widget.component.html',
styleUrls: ['./blocksurvey-widget.component.scss'],
encapsulation: ViewEncapsulation.Emulated
})
- We need to use emulated in order to get styling from the parent component. Otherwise, if we don’t want any styling, we can use ShadowDom.
- Now we can add the html content in blocksurvey-widget.html and styling in blocksurvey-widget.scss. This will show how our widget should look and what all will it show.
- The next step is to concatenate all the files that are being created while building the Custom elements. For that, we need to install fs-extra and concat npm modules.
npm install fs-extra concat --save-dev
- Now create a file known as build-script.js or any name as you wish to concatenate all the files together into one file. The output file will be the script file which has to be used with the custom elements. Copy the below content into that file.
const fs = require('fs-extra');
const concat = require('concat'); (async function build() {
const files = [
'./dist/blocksurvey-widget/main-es2015.js',
'./dist/blocksurvey-widget/polyfills-es2015.js',
'./dist/blocksurvey-widget/scripts.js'
]await fs.ensureDir('elements')
await concat(files, 'elements/blocksurvey-widget.js')})()
- Make sure whatever file we are concatenating should be present in the dist folder after building, otherwise it will throw an error such as missing file.
- Install ngx-build-plus npm module for giving a Single Build.
ng add ngx-build-plus
- In package.json make changes. Add the below line in the scripts section :
"build:elements": "ng build --prod --outputHashing none --single-bundle && node build-script.js"
- This single bundle will remove runtime-es2015.js file.
- Next step is to build the custom element. Use the below command :
npm run build:elements
- This will create a folder called Elements which will have the main script file which contains the details of custom element which we created. This has to be used with Custom element tag.
<blocksurvey-widget></blocksurvey-widget>
<script type="text/javascript" src="path-to/blocksurvey-widget.js"></script>
Implementation of Widget in BlockSurvey :
BlockSurvey provides a way to embed surveys/polls/forms using widgets to any external webpage. You can see the demo of it by signing to BlockSurvey.
* Create a survey and then publish it from the share screen. Use ‘Create Widget’ link for embedding widget and for customizing the widget.
Screenshots of the widget implemented in BlockSurvey :
- Dashboard for Widget Embedding :
- Toggling of Survey using Widget :
- Customization of Widget and Script for Embedding :
Summary :
Building custom elements with Angular is pretty straight forward, as we can see. Although, there are some manual things that we need to do, which ends up being a simple task.
Custom Elements helps to build sharable components like widgets for products to expose features outside the app or product. Definitely, it helps in the growth of the product and makes it really flexible for users to use your product in many ways.
Also as we can see the size of the output file is quite small, considering that it has Angular dependencies inside of it. Angular 9 provides ways to reduce the file size for the generated script file. Let’s hope that in the future releases this process gets even easier.
Well, I have shared my views as a developer. Please share your thoughts in the comments below!
References :
About Blocksurvey
BlockSurvey is a privacy-focused platform to create surveys, polls, & forms with complete confidentiality. Through BlockSurvey, all your data is encrypted end to end and only you can see it. You own your data. It’s your digital right. There are zero trackers and we keep you as anonymous to the data collectors. Our platform utilizes Blockstack and helps in maintaining privacy and anonymity, resulting in effective surveys and polls. Try out by creating your surveys and polls with us.