Adding event listeners outside of the Angular zone

Wojciech Trawiński
JavaScript everyday
3 min readJul 7, 2019

If you are familiar with Angular framework, you should be aware that every asynchronous event triggers the change detection process by default. In some scenarios you don’t need to think about it at all, it simply works as expected. However, there are situations when running the change detection process too often may result in poor runtime performance 🐌. In this article I’ll present how to properly add event listeners outside of the Angular zone, so that the change detection is not triggered.

Getting started

Let’s start with a simple example just to make sure that we are on the same page. Take a look at this exemplary component:

In the above example, I want to simulate drawing a line on the canvas every 2 seconds. Canvas manipulation does not require bindings update (and it happens as a part of the change detection process), therefore there is no need to run the change detection process everytime I call the drawLine method. Unfortunately, this is not the case in the above example, since I do not run the setInterval method outside of the Angular zone. As a result every 2 seconds, the change detection is triggered which can be confirmed by the console log performed within the getLabel method.

In order to improve performance (not trigger the change detection process every 2 seconds, since there is no need to update bindings), you simply need to run the method triggering the change detection outside of the Angular zone, namely:

With the solution, calling the drawLine method will not trigger the change detection, therefore the getLabel method will not be called every 2 seconds.

Event listeners

Let’s now focus on event listeners. The component has the following code:

Assume that you want to draw a line on the canvas based on mousemove events. If you do not care about runtime performance, you can accomplish the goal as in the above example (simply bind to the appropriate event within the template). However, it results in the same problem as with the setInterval, namely the change detection is triggered everytime a mousemove event is dispatched. As a result the getLabel method is called unnecessarily.

In order to improve the solution, you can run the code outside of the Angular zone. However, if you do not understand how things work under the hood, you can try the following:

Unfortunately, it still triggers the change detection process everytime a mousemove event is fired.

In order to add a mousemove event listener, so that the change detection process is not triggered once an event has been dispatch, you need to imperatively add an event listener with the aid of the Renderer2 instance:

With the above solution, a mousemove event does not trigger the change detection process. The code is a little bit imperative and you need to take care of releasing resources by calling the callback returned from the listen method.

Being a developer you should always take care about performance. Sometimes it’s all about monitoring metrics, but there are situation when you need to use the force 🌟 and optimize a part of your code. When it comes to Angular, running a certain code outside of the Angular zone is a low-hanging fruit. You can take advantage of it even when you need to update bindings, but you simply want to do it less often (simply inject the ChangeDectector instance and call an appropriate method).

I hope you liked this post and learned something new 👍 If so, please give me some applause 👏

--

--