February 5, 2024

Angular Signals Part 1 – How-to guide on Angular Signals

Klicke hier für die deutsche Version des Blog-Posts.

Enhancing Performance in Angular 17 with Signals: A Smart Solution

In the evolution of Angular, the adoption of Signals introduces a simpler and more efficient approach to change detection, fundamentally enhancing application performance and reactivity. Signals were introduced in Angular Version 16 as a developer Preview. Angular 17 doubles down and delivers the feature in a stable version. Unlike the comprehensive scanning strategy employed by Zone.js, which checks the entire component tree for changes, Signals employ a more targeted method. They directly update only the components affected by state changes, significantly reducing unnecessary performance overhead.

This shift not only streamlines Angular’s change detection mechanism but also aligns with a more intuitive model of reactivity, improving both the development experience and the framework’s responsiveness. Through Signals, Angular developers gain access to a more precise and efficient toolset for managing state changes, marking a significant step forward in the framework’s capability to handle dynamic and complex applications with ease.

In essence by offering a more efficient, targeted, and simplified model of reactivity, Signals enable Angular applications to achieve higher performance levels and provide developers with a more streamlined and effective toolset for building dynamic, responsive applications.

If you want to learn all about Signals in Detail and find out about all the tipps and tricks, come to one of our Angular Courses:

Which problem is solved by signals?

Angular’s Signals are engineered to elevate application runtime performance by replacing Zone.js. Traditionally, Zone.js played a crucial role in activating Change Detection and refreshing the UI whenever the application’s state changed. This method required scanning the entire component tree to identify relevant state changes, often leading to performance lags due to redundant checks on inactive components.

The introduction of Signals renders the comprehensive component tree scans unnecessary. Now, only the components directly impacted by a change are updated, significantly streamlining the DOM update process. This focused strategy not only minimizes system overhead but also boosts the application’s overall efficiency.

Beyond enhancing performance, Angular’s Signals also simplify state management, offering a more intuitive alternative to RxJS, especially when replacing Subject-based state. RxJS, with its powerful but complex set of tools for reactive programming, can be challenging for developers to master, especially when managing application state through Subjects. Subjects in RxJS, while flexible, require a deeper understanding of reactive programming concepts, such as observables and subscribers, making them less accessible for beginners or for simpler applications.

In contrast, Signals provide a straightforward mechanism for state management. They eliminate the steep learning curve associated with RxJS by offering a simpler, more direct approach. Developers can define a state and directly link it to the UI components, bypassing the complexities of observable streams. This direct linkage simplifies the process of updating the UI in response to state changes, as there’s no need to manage subscriptions or handle streams of data. With Signals, state management becomes more about defining and reacting to state changes, rather than juggling the intricacies of reactive programming paradigms.

 

Signals aka Reactive Primitives

Signals, known as Reactive Primitives, are a system that tracks the use and dependencies of state within an application, enabling Angular to optimize rendering updates. With Signals, Angular precisely identifies where state is used and its dependencies. This allows for targeted re-rendering of components, reducing the need for exhaustive checks and eliminating the reliance on Change Detection. Unlike Observables, Signals don’t require subscriptions and always hold an initial value, simplifying state management by removing the need for asynchronous handling, such as the async pipe.

Signals are thoroughly typed and can be of type Number or String or even complex types. They can be writeable or readonly and you can always create a readonly signal from any writeable signal with .asReadonly(). Readonly signals can also depend on writable signals but we will look at that later.

It’s also very easy to export signals in order to use them in several components.

How to interact with Signals

So now that we’ve established the benefits of Signals in tackling common challenges in Angular development, it’s time to delve deeper. Let’s explore how to utilize them effectively and examine the various methods of interaction available at our disposal.

signal()

By calling the signal function you can create a writeable signal. In our case, a counter.

We can access the value with the variable name and round brackets. This also works in the template if you use it in an expression.

set()

With set you can give the signal a new value.

We can use this to reset the counter to zero.

update()

By using update you can also change the value of the signal, but now you have access to the current value. So you can set a new value based on the old one.
In our example we can use this for an increment or decrement function.

The update function doesn’t have to be a one-liner. As long as you specify a return value, you can perform various operations.

computed()

To create a signal that is based on or dependent on another signal you can use computed. This function generates a readonly signal that updates itself if the value of the signal on which it depends changes.
As an example we will make a variable that checks if the current counter is even or odd. isOdd cannot be changed through the set or update function. But since Angular knows that there is a dependency between the two signals, every time the counter signal changes the callback function of isOdd is executed again.

Of course this also works with two or more signals to depend on. Now combined is updated every time either firstLetter or secondLetter changes.

As with the update function computed doesn’t have to be one line. But be aware that the dependencies of a computed signal are not only determined by its return value. In the example below combined now only uses firstLetter in the return statement, but it’s still getting updated when the value of secondLetter changes since the signal is used in the callback function of combined.

effect()

With effect you can declare what should happen if the value of a signal changes or in other words which side effects are triggered by this. That can be logging the value of a signal, exporting the value to localStorage or saving the value transparently to the database.
In our case we just want to print the new counter value to the console.

By default, registering a new effect with the effect function requires an injection context (access to the inject function). The easiest way to provide this is to call effect within a component, directive, or service constructor. Alternatively, the effect can be assigned to a variable (which also gives it a descriptive name).

Like the computed function an effect can have a dependency to multiple signals.

untracked()

If you want to read signals in a reactive function such as computed or effect without creating a dependency you can prevent a signal read from being tracked by calling it with the untracked function.

Let’s take the letterEffect from above as an example. At the moment the effect logs the current letters when either one of the signal values changes. If the effect should only be triggered when the firstLetter changes, but not when the secondLetter changes we can write the following:

Experience is the Best Teacher: Hands-On with Signals

Ready to put theory into practice? In this interactive StackBlitz example, we’ve crafted a sample app that embodies the Signal concepts we discussed. Feel free to dive in and experiment to see Signals in action!

Use cases of Signals

Signals are well suited to manage a synchronous state in the components. They don’t for events and other asynchronous operations. Therefore signals are an extension to RxJS and not a replacement. They can be a substitution for async pipes and OnPush components as Angular notices by itself when something in the component changed.

More about the usage of Signals and RxJS will be covered in part two of our angular signals article series.

If you want to dive deeper into the what, why and how of Angular Signals we really recommend the Videos of Deborah Kurata. She does an exceptional job in explaining the concepts behind the new reactive primitive.

 

Related Posts

theCodeCampus Autorin Anne Naumann

Anne Naumann
Developer at thecodecampus </>

Hi, I'm a web developer with a focus on frontend technologies, especially Angular. I also have a lot of fun when it comes to UI/UX and when I need to make room for new books on my bookshelves.


Leave a Reply

Add code to your comment in Markdown syntax.
Like this:
`inline example`

```
code block
example
```

Your email address will not be published.