Understanding Angular Signals: A Deep Dive into Granular State Tracking

Angular Signals is a powerful system that provides granular tracking of how and where your application’s state is utilized, enabling Angular to optimize rendering updates efficiently. In this blog post, we’ll explore the concept of signals, their types, and how they play a crucial role in managing state within Angular applications.

What are Signals?

A signal in Angular is essentially a wrapper around a value that notifies interested consumers when that value undergoes a change. These signals can encapsulate a wide range of data, from simple primitives to more complex data structures. Importantly, the value of a signal is accessed through a getter function, allowing Angular to track its usage throughout the application.

Writable Signals

Writable signals provide an API for directly updating their values. You create writable signals by invoking the signal function with the initial value. For example:

const count = signal(0);

// Reading the value using the getter function
console.log('The count is: ' + count());

// Updating the value directly
count.set(3);

// Updating the value through the update operation
count.update(value => value + 1);

Computed Signals

Computed signals derive their values from other signals and are defined using the computed function, specifying a derivation function. They are both lazily evaluated and memoized, meaning their values are calculated only when needed and are cached for future reads:

const count = signal(0);

const doubleCount = computed(() => count() * 2);

Notably, computed signals are not writable, and attempting to set a value directly will result in a compilation error.

Dynamic Dependencies

Computed signal dependencies are dynamic, meaning only the signals actually read during the derivation are tracked. This flexibility allows for efficient computation and caching of values. For instance:

const showCount = signal(false);

const count = signal(0);

const conditionalCount = computed(() => {
  if (showCount()) {
    return `The count is ${count()}.`;
  } else {
    return 'Nothing to see here!';
  }
});

In this example, if showCount is false, updates to count will not trigger recomputation. However, if showCount is later set to true, the computation will re-execute, taking the branch where count is read, and updates to count will invalidate the cached value of conditionalCount.

Conclusion

Angular Signals provide a flexible and efficient mechanism for managing state in your Angular applications. By understanding the types of signals available, their properties, and how they interact, you can leverage this system to create more responsive and optimized Angular applications.