Event Dispatch in Angular

Angular
Angular Blog
Published in
3 min readApr 22, 2024

--

Authors: Jatin Ramanathan, Tom Wilkinson

Photo of a telephone switchboard by Joe Mabel, CC BY-SA 4.0, via Wikimedia Commons
Photo of a telephone switchboard by Joe Mabel, CC BY-SA 4.0, via Wikimedia Commons

Rather than focusing on developer-facing APIs, in this blog post we’ll cover under-the-hood details of the new event delegation system of Angular. In the long-term it’ll enable features such as fine-grained hydration and improve runtime performance by reducing the number of event listeners in your apps.

This initiative is inspired by the convergence of Angular and Wiz and sets the foundation for the resumability that Wiz offers.

Background

Our most recent update about server side rendering announced that full application hydration was launching in Angular v16. Since then we’ve been focusing on making further improvements to the server side rendering in Angular..

One issue with full application hydration is that your page may look interactive before hydration has happened. When HTML is rendered on the server, it is visible as soon as the page loads. This means the user may assume that they can interact with the page when in reality none of the JavaScript event listeners have yet been loaded and attached. Loading the JavaScript can involve a round trip to a server if it isn’t cached. Furthermore, hydration itself can take awhile depending on how much JavaScript needs to run.

When the page looks interactive but doesn’t respond to user actions, it creates a frustrating user experience. For instance, if a button is visible but nothing happens when the user clicks on it, it can result in rage clicking. One strategy that applications take to improve this user experience is to capture all events at the root of the application and replay them once hydration is finished. We refer to this feature as Event Dispatch.

The user experience is better since it turns ‘unresponsive’ into merely ‘delayed’. It helps users to notice that the application will eventually respond to their interactions and they don’t need to repeatedly tap to make sure their interaction eventually goes through.

Five years ago, before hydration support was available, the community implemented a similar approach in Angular via Angular preboot. Rather than resurrecting preboot, we’ve decided to leverage the collaboration with Wiz. Wiz provides the Event Dispatch capability to hundreds of Google applications (including google.com) using a library called JSAction. The library is over a decade old and incredibly well battle-tested. This library also enables additional capabilities required for fine-grained hydration.

How Event Dispatch works

As an example, consider the following DOM tree that contains a button.

A tree diagram showing the relationship between HTML elements. The words “body”, “ul”, “div”, “span”, and “button” are arranged in a circle on a white background. “body” is at the top of the circle. “ul” and “div” branch out from the sides of “body”. “li”, “span”, and “button” branch out from the sides of “div”. The image depicts the hierarchical structure of HTML elements, where the <body> element is the root element and all other elements are nested within it. The <ul> element represents an u
Example DOM hierarchy with a button

The template that was used to render the button may look like this:

<button (click)=”onSave()”>Save</button>

Normally, the onSave() method would only be called once your application had finished hydration. If Event Dispatch is enabled, JSAction is listening at the root of the application. The library will capture events that bubble to the root and replay them once hydration is complete.

A diagram illustrating event propagation in HTML. Text at the top reads “body jsaction listening at the root”. Below is the text “Regular event bubbling”. A tree structure branches out below, showing the relationship between HTML elements. The root of the tree is labelled “body”. Text beside it reads “jsaction listening at the root”. Branching from “body” is a shape labelled “ul”. Branching from “ul” is a shape labelled “li”. Branching from “body” is another shape labelled “div”. Branching fro
JSAction listens at the root to capture events that bubble

In this way, Angular applications will stop dropping events before hydration is complete and allow users to interact with the page as soon as the page loads. Angular will use JSAction under the hood. There will be no need for developers to do anything special beyond enabling this feature using public APIs that will be introduced in a future blog post.

What’s Ahead

Integrating Event Dispatch is a key first step towards finer-grained hydration. With Event Dispatch we stop dropping events that trigger before hydration. However, we still need to wait for hydration to finish to respond to the events. In order to minimize this wait time we need to decrease the amount of JavaScript that is in the critical path of hydration. We plan to do this by lazily hydrating Deferrable Views that are rendered on the server. Stay tuned for more exciting updates in server side rendering!

--

--