Lifting the Veil: Insights into Angular’s EventManagerPlugin

Netanel Basal
Netanel Basal
Published in
3 min readFeb 12, 2019

--

In this article, I will walk you through the steps of extending the Angular events system and providing your own custom events. But first we need to understand how Angular manages events.

When we add an event by using one of the following methods:

  1. Register it on to the template by using Angular event bindings
  2. Using the HostListener() decorator

Angular calls the Renderer listen() method. Let’s see what this method does:

We can see that it actually delegates the event registration to something called the eventManager. The EventManager is an injectable service that provides event management for Angular through a browser plug-in.

A plugin is just an object that needs to implement two methods:

  1. supports() — This method determines if this plugin should handle the current event. It receives the event name and should return a boolean.
  2. addEventListener()— The function that will be called when Angular registers the event. This function accepts three arguments — the target element, the event name, and the handler function. This method should return a disposable function that will be called when Angular removes the element from the DOM.

There is also an optional third method called addGlobalEventListener() that is used when Angular should handle events on global elements, for example, (window:scroll).

In order to register a plugin, we need to provide it via DI to the EVENT_MANAGER_PLUGINS provider:

Angular uses the multi-option to support multiple plugins. If we take a look at the Angular source code, we’ll see that Angular already comes with a couple of built-in plugins that handle common events we’re all familiar with. For example — KeyEventsPlugin.

Now that we understand the basics, let’s examine the EventManager functionality:

We can learn few things from the code above. First, Angular sets the manager property on runtime so we can use it in our plugin to retrieve the compilation zone in which event listeners are registered (later, we’ll see how we use it).

Next, Angular reverses the plugin order. (Yes, you know why).

Finally, the addEventListener() searches for a matching plugin and delegates the work to it. Let’s understand the _findPluginFor() method:

The _findPluginFor() method retrieves the registered plugins from the EVENT_MANAGER_PLUGINS provider, searches through them by calling their supports() method, passing on the event name to find a plugin that supports this type of event.

When a valid plugin is found, the manager uses that plugin’s version of addEventListener(); otherwise, it throws an error.

And that’s all there is to it. Now, let’s review some useful examples for creating our own plugins. Say we want to add the functionality to debounce or throttle events.

For example, we want to provide the following functionality:

Let’s create a DeferEventsPlugin. (I know, you can choose a better name)

As we learned before, we need to implement the supports() method that in our case, tests if the event name contains the words throttle or debounce. If it does, Angular will invoke our plugin’s addEventListener() implementation.

Next, we register an event based on the method name, returning a function that when called, removes the event to prevent memory leaks.

Pay attention to the optimization we introduce here. The only code that runs inside the Angular zone is the handler that will be invoked by the debounce or throttle methods.

I will leave the addGlobalEventListener() implementation to you.

Let me give you some more ideas for plugins. I’ve already written about creating the functionality to run events outside the Angular zone by creating a directive, but we can also easily achieve this with a custom event plugin, for example:

Let’s finish by implementing the event delegation pattern. We can register one event listener at the document level and target the current element. Btw, if I remember correctly, that’s a pattern used by React. Here’s a pseudo-code that will give you this kind of functionality for click events:

And we can use it like the that:

Note that I’m using POJO for brevity. That’s all.

Follow me on Medium or Twitter to read more about Angular, Elf and JS!

--

--

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.