Introduction to Signals

Since the angular introduction in 2016, observables and RxJS have been the major ways of managing states and sharing data in angular applications. despite the observables and RxJS having huge capabilities and well-crafted design patterns, it has a steep learning curve.

Angular relies heavily on a library called zone.js to detect any changes that can happen in an angular application, it means that for any minor change in the project, the zone.js library will need to execute the changes throughout the entire component tree which brings negative effect on the angular project and makes the bundle size of the application bigger this combination reduces productivity. The bigger the application gets, the more time it takes to compile the application which reduces productivity.

Generally, zone.js is great, it detects changes and updates the state in the components tree automatically without us handling it. this is fantastic until it isn’t. Large applications at scale with hundreds of components, hundreds of events, and lots of data changing become problematic.

One of many exciting new features in angular is signals. With signals, we can build complete zoneless applications that do not rely on zone.js to detect changes. which will improve performance, reduce bundle size, and improve productivity.

Learning signals and building Angular applications with signal-based reactivity gives you a great skill advantage since you can use this skill to work with other frameworks that use signals too.

The signal is a function that we now have in angular, which is named reactive primitive. Currently, we have 3 types of reactive primitives:

  • Signal
  • Computed
  • Effect

Side Note: I recommend reading the section “Reactive Development” in my article named “RxJS: What and Why”. it will help you understand what reactivity means.

Signal

The signal can notify other places that use the variable that something has changed and that they should update their too. so if you get the idea, instead with zone.js to search the component tree where the change affects, with signals we know exactly what changed. we have more fine-grain update control ability.

Let’s see a simple example of using a signal:


@Component({
  template: `

{{firstname()}}, {{lastname()}}

` }) export class MyComponent { firstName = signal('Nisan'); lastname = signal('Sabag'); }

Computed

A computed signal lets you have an updated signal value based on other signal values. it’s a read-only value, you can’t set it directly.

Let’s see a simple example of using computed:


@Component({
  template: `

{{fullName()}}

` }) export class MyComponent { firstName = signal('Nisan'); lastname = signal('Sabag'); //derivaition function fullName = computed(() => `${firstName() ${lastName()}`); }

From a performance point of view, this is awesome because we only recalculate when something changes (memoization and caching). we don’t have to use state management libraries like redux to manage states anymore (unless you need advanced specific stuff). now you can use signals that stay up to date and with a combination of services you are set to manage state all over the application.

Effect

An effect is a side-effect operation that reads the value of signals. When the signal changes, we can do some behavior like calling a function. The effect takes a function, let’s see a simple example:


@Component({
  template: `

{{fullName()}}

` }) export class MyComponent { firstName = signal('Nisan'); lastname = signal('Sabag'); effect(() => console.log('something have changed!!' + lastName())); }

Signals can notify interested consumers when they change and the effect is an operation that runs whenever one or more signals change.

With effects, we can log data for analytics or debugging. keeping data in sync with window.localStorage and much more.