Native HTML dialog in Angular with dialog-polyfill

Ernesto Mancebo
Aug 25, 2018 · 4 min read

Finally we have a browser native dialog element 🎉

Bad Bunny — Source: giphy.com

Why the excitement? well, I’m the kind of software engineer who rather use a non-framework/dark magic approach, because it frees me from doing an adaptation for failing cases, carry a bunch of peer dependencies, doing workarounds for expected implementations, wait until a third fix an error (or merge a pull request on github) and so on.

Why a native implementation is good if implementations from thirds does the same job (for example a modal)? Well my dear, both does the job, but a native implementation is a standard one, don’t depend upon dependencies that might conflict with my app dependencies (been there, done that), fully functional on its environment (browser in this case) which will always work because its implementation was tested against a standard design.

But that’s the explanation about my joy, I don’t have to be right. Let’s go to the gravy.


TL;DR

  1. Install the dialog-polyfill package.
  2. Create the dialog Angular wrapper-component.
  3. Import the dialog-polyfill library into the component.
  4. Include the dialog-polyfill.css into your deliverable HTML file.
  5. Insert a template tag into the root component HTML which will host the dialog.
  6. Put a function in the root component that listen to an event and create dynamically the dialog component.
  7. Include the dialog wrapper-component into the module entry components.
  8. In the dialog onInit function, get the dialog element reference and register the dialog.
  9. Show the modal. 😌

At the moment of this writing, the dialog-polyfill version is 0.4.10, I’ll try to keep this post up to date as the library implementation evolves. Also the version of Angular I’m using is the 6.

I’m assuming you already have an angular app created, thus I’m skipping that step.

The sample project is here:

Let’s get into this

Currently the dialog element is not supported in all browsers, that’s why we need the Google Chrome dev team polyfill , so let’s install it.

yarn add dialog-polyfill

or

npm install dialog-polyfill --save

Now let’s create a dialog wrapper-component in our angular app that will hold a message and contains two buttons: ok and cancel.

If you’re using ng-cli, just: ng g c components/dialog

In the HTML template create the dialog as follow:

dialog.component.html<dialog class="my-dialog" (close)="handleClose($event)" #leDialog>
<h3>{{notification.title}}</h3>
<p>{{notification.message}}</p>
<div class="inline-components">
<button class="btn" (click)="okClicked()">ok</button>
<button class="btn" (click)="cancelClicked()">cancel</button>
</div>
</dialog>

Note that we can be subscribed to the close event in the dialog which contains the returnValue property. That property contains the value passed to the dialog.close() function as parameter.

At this moment, jump into the component class import the library, get the reference to the dialog element in the HTML (#leDialog in this example); finally in the ngOnInitfunction, use the reference to bootstrap the dialog following display it. See it here:

dialog.component.ts// some imports
import * as dialogPolyfill from 'dialog-polyfill/dialog-polyfill';
@Input()
notification: Notification;
@Output()
close: EventEmitter<any> = new EventEmitter();
@ViewChild('leDialog')
private dialogElement: ElementRef;
private nativeDialogElement: any;
ngOnInit() {
dialogPolyfill.registerDialog(this.dialogElement.nativeElement);
this.nativeDialogElement = this.dialogElement.nativeElement;
this.nativeDialogElement.showModal();
}

Then, register this component in the entryComponents array in the NgModule you’re working on. Following this, we must include the dialog-polyfill.css file in the HTML head.

BONUS 🎁: I’m using ng-cli, is one of the most wide-used packaging tool for Angular 2+ apps, if you don’t know how to include a CSS library installed via npm/yarn or downloaded from the web into your bundle; this is how:

In the angular.json file, there’s a key called styles, the path: projects > your-app > architect > build > options > styles there include the path to the CSS, in this case: node_modules/dialog-polyfill/dialog-polyfill.css

The fifth step is to include a template tag in the root element. It’s required to be in the top of the document because in the dialog-polyfill documentation they specify that the dialog must be hosted with no parent of top of it.

In the root component (app.component.html and app.component.ts in this case), include the template tag that would hold the dialog and then gather the reference to it.

app.component.html// some html code
<template #dialogContainer></template>
// more html code

At this moment we have to include in the app.component.ts a function that listens to an action which will grab the #dialogContainer and inject the DialogComponent dynamically. Remember that such component has in the onInit function the code that will display the dialog 👍.


app.component.ts
// some wonderful imports@ViewChild('dialogContainer', { read: ViewContainerRef })
private containerComponent;
private dialogContainerRef: ComponentRef<DialogComponent>;
private closeEmitterListener: EventEmitter<any>;
dialogResult = '';
constructor(private resolver: ComponentFactoryResolver) { }ngOnDestroy() {
this.dialogContainerRef.destroy();
if (this.closeEmitterListener) {
this.closeEmitterListener.unsubscribe();
}
}
displayDialog(notification: Notification) {
this.containerComponent.clear();
const factory: ComponentFactory<DialogComponent> = this.resolver.resolveComponentFactory(DialogComponent);
this.dialogContainerRef = this.containerComponent.createComponent(factory);
this.dialogContainerRef.instance.notification = notification;
// Subscribes to the close EventEmitter and listens to the value returned by the dialog wrapper-component
this.dialogContainerRef.instance.close.subscribe(value => {
this.dialogResult = value.returnValue;
});
}

You can check out the post of Netanel Basal which has a great explanation of the logic of creating dynamic components for further details.

And that’s it, that’s all regarding creating a native dialog, now we need a classic button or listen to an event which calls the function and trigger the dialog.

hooray 🙌

To get the complete code of this guide please look into the sample project.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade