Auto-filtering a dropdown list with datalist

At times, we developers are so focused on Angular and TypeScript that we forget that even the basics, such as HTML and CSS, are always evolving. In that spirit, I’ve covered some hidden gems of HTML in the past.

For instance, I’ve seen numerous consulting clients use a specific library for fancy auto-filter or auto-complete dropdowns. While the approach works, adding too many dependencies is the number 1 reason people get stuck with an old version of Angular.

Enter datalist, a simple HTML element that auto-suggests and filters based on user input:

How’s that for a low-code, no-dependency solution? datalist works very much like a select element, using a list of options to be rendered. What connects the datalist to an input is the list attribute on the input that refers to the id of the datalist element:

And that’s it. On a side note, datalist is also compatible with many other input types, such as time or color:

This results in:

What’s new in Angular 18?

The Angular team announced the release of Angular 18 earlier today. You can read the official announcement for in-depth updates. In the meantime, I thought I would summarize the top 6 highlights of this new version here:

  • New events Observable available on form controls, which returns any event going on in that control, including the different form state properties (valid, pristine, etc.)

  • Replaying events with server-side rendering. When using SSR, any user actions that happen before the corresponding Javascript handler is downloaded can be replayed automatically using the provider: provideClientHydration(withEventReplay())

As always with major versions, there’s more, but this overview of Angular v18 should give you enough to get started.

Signals: Effect cleanup

You’re probably familiar with ngOnDestroy for Angular components/pipes/directives, which cleans things up when an Angular object is destroyed. A typical use case is to unsubscribe from RxJs subscriptions (though there are better-suited tools for this now) or cancel a timer.

Angular Signals introduced the effect() function to run side-effects, including RxJs Observables or timers. As a result, we might want to cancel a timer or an RxJS-based task when the effect runs again in the future, and luckily for us, Angular supports a cleanup function for our effects.

To enable the cleanup function, we pass it as a parameter to the callback registered in our effect:

Then we call it and pass our cleanup code as a param to it using an arrow function:

Here is the full example in action on Stackblitz. This example destroys a component and clears its timer thanks to the effect cleanup function:

Signals custom equality functions

Angular Signals are growing almost every day with new features and new ways of building Angular applications. If you’re still unsure about signals and want to catch up, you can watch this Angular Signals workshop recording on Youtube or sign up for my continuously updated Angular Signals course.

Today, let’s explore how Angular determines if the value of a signal has changed. If we create the following signal with a default value of “Hello”:

Then calling name.set("Hello") does not trigger any updates for any of the consumers of our name signal. That’s because Angular compares the “new” value with the previous one using a === comparison, and "Hello" === "Hello" is true, so Angular doesn’t trigger any change.

What about objects? When comparing objects with ===, we’re comparing references, meaning “Are the two variables pointing at the same object?” We can verify that easily with a few lines of Javascript running in a browser console:

This leads us to the custom signal equality function. If we want to do a deep comparison instead of just comparing references, we can instruct Angular to do so by providing our own comparison function. Here is the example used by the Angular team:

Of course, we don’t need lodash to implement custom equality functions. For example, we want to create a signal that only emits updates when the current user ID changes. We don’t care if the user’s name or email changes, just the ID (which would indicate that a new user logged in, most likely):

With this approach, the signal doesn’t trigger any updates if we change values that aren’t the ID. For instance, we can secretly turn our user into Batman (and note that I do create a new object by making a copy of the current one, which would make === return false):

And yet my user remains known as Bruce after turning him into Batman (full example here on Stackblitz):

Why use custom equality functions?

Performance could be one thing: if we have a signal that stores an object and know that the only change we make to that object is one or two specific properties, then creating a custom equality function on these properties would make perfect sense. Even more so if the properties are in nested objects, though I wouldn’t recommend using deeply nested objects as signal values, keeping our values as flat as possible instead.