Creating Custom DOM Events in Angular2

AKA: Convert Ben Nadel’s ES5 Code to TypeScript

All of the credit for the 4 hours I spent obsessing on this research is thanks to Ben Nadel and his recently posted on Creating Custom DOM And Host Event Bindings In Angular 2 Beta 6.

I was crazy enough to be the first human to attempt to convert this ES5 article and ‘transpile’ it back to TypeScript so people can have a better understanding of how the hell he created these cool custom events.

You can find a working example of this code on my Angular2-Webpack-Lite repo on my `experiment` branch.

In a tl;dr fashion I wanted to paint a picture of the use case for an custom event like this:

You are creating a modal that a user will use, and you want to call your `closeModal()` function when the user clicks outside of the modal element.

To do something like this you can extend the functionality of the EventManagerPlugin.

Image for post
Image for post
EventManagerPlugin definitions file. Helpful for seeing what properties are implemented.

While also doing a little search-sky I found another class which already extends EventManagerPlugin called DomEventsPlugin which was already using the abstracted DOM token for platform safety. (IE Webworkers, Servers, etc.)

Image for post
Image for post
DomEventsPlugin definitions file. Simpler to extend. Less work involved since I’m already wanting a custom DomEvent.

DomEventsPlugin decided to be an easier class to extend since all the extra work of extending EventManagerPlugin had already been taken care of.

I wasn’t quite sure what the difference was between addEventListener() and addGlobalEventLisenter() until I started to hack around with it. What I found was:

addEventListener:

Fires when you use events from the DOM:

addGlobalEventListener:

Fires when you use events from anywhere (including seen here in the hosts tab.

So as Ben Nadel, the muse of this writing wrote, we need to essentially have an eventMap which will allow us to listen to the original event, while still providing the custom event name.

Image for post
Image for post
Our custom events map property.

Angular2 determins an event is valid when one of the EventManagerPlugin extensions in EVENT_MANAGER_PLUGINS returns true for its required supports() function:

Image for post
Image for post
`supports()` determines whether or not (clickOutside) is a valid event name.

Finally I implemented the addEventListener() and addGlobalEventListener() functions:

Image for post
Image for post
For more information about Zones and why we I am using them when triggering these events, you can refer to the article at the top of the page.

Thankfully thanks to DomEventsPlugin, most of this code was a copy paste job. A few changes that I made were:

  • For addEventListener(), use event delegation instead of binding directly to the event (with DOM.getGlobalEventTarget(‘document’)). This let me return a reference to document and bind it to the event.
  • Then, I prevent the click event from occurring if the clicked element IS the same as the element being bound to.
  • For DOM to not be null when my code ran, I had to pull it from ‘angular2/src/platform/dom/dom_adapter’ instead of ‘angular2/platform/common_dom’. For some reason TypeScript is breaking DOM somehow making it null.

Finally! (tl;dr / show me the code damnit):

In conclusion this is the final result that I have returned with:

Ben Nadel would be proud.

Usage:

To include this into EVENT_MANAGER_PLUGINS, you need to use provide():

And then you will import those constants into your bootstrap() call:

This is where I add it to my app.

Conclusion:

If Ben Nadel keeps pumping out things no one has done before with Angular2 in ES5, then you’ll keep seeing me publish articles on it. Okay, well maybe I’ll just write Angular2 articles anyways.

Thanks for reading and feel free and catch me on Github/Twitter @TheLarkInn.

For more information about Zones and why we are using them when triggering these events, you can refer to the article linked at the top of the page.

Written by

@Webpack Core team & AngularCLI team. Program Manager @Microsoft @EdgeDevTools. Web Performance, JavaScripter, Woodworker, 🐓 Farmer, and Gardener!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store