Angular - Signals



This chapter will discuss Angular Signals, a new way to build Angular applications. In this, we will discuss what exactly Signals are, why they are really useful in Angular, and how they can improve your application by providing a simpler and more efficient approach.

The chapter will also highlight the advantages of using Signals in Angular development.

What are Signals in Angular?

A Signal is a wrapper around a value that notifies interested consumers when that value changes. A signal can contain any value, from primitives to complex data structures.

In Angular, Signals are a system (or reactive primitives) that granularly (closely) tracks and updates how and where your state is used throughout the application, allowing the framework to optimize rendering updates.

When a signal value changes, it automatically triggers updates to any dependent components or services that are observing it. For example:

// Declaring a regular variable
count1: number = 0;

// Declaring a variable using signals
count2 = signal(0);

In this case, count1 will not automatically reflect the latest value when it changes. However, count2, which is a signal, will automatically update and reflect the latest value whenever it changes.

Important! In Angular, the signals were introduced in version 16.0 and became stable in version 17.0.

Note:  To read a signal, you need to call its getter function, which allows angular to track where the signal is used. The signals in angular may be either writable or read-only.

Reactive Primitives in Angular Signals

Here is a list of the commonly used Signals or reactive primitives (constructs) in Angular:

Writable Signals

In Angular, the writable signals are specific types of signals that allow you to "modify the value directly". In addition, they are used to represent the value that can be changed.

These signals provide an API for "updating their values directly". You can create writable signals by calling the signal function with the signal's initial value.

Syntax

Following is the syntax of the Angular Writable Signal

count = signal(initial_value = 0);

Here,

  • The count is a variable whose "initial signal value" is 0 (can be any value).
  • Signal() is a constructor that "defines the writable signal".

Here are "two" commonly used methods to set or update the writable signal value:

  • set()
  • update()

The set() method

To "change" the value of the writable signal, you can use the set() method to set it directly.

Syntax

Below is the syntax of the set() method −

//Initializing a signal with an initial value
count.set(initial_value);

Here, count is a variable for which we 'set' the initial_value.

The update() method

You can use the update() method or operation to compute a "new value" from the "previous one".

Syntax

Below is the syntax of the update() method −

count.update(value => value + new_value);

Here, the value is the previous "value", which will be updated by adding a new_value in it.

Note! The writable signals have the type WritableSignal.

Example of Writable Signal in Angular

We create a Signal with an initial value of 0 using the signal(0) constructor. We will use the set() and update() methods in different functions to "increase" and "decrease" the count value on each click of the given button −

TypeScript code (component.ts)

import { Component, OnInit, Signal, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent{
  //declaring a singnal
  count = signal(0);
  increase(){
    //using .set() method to increase count value
    this.count.set(this.count()+1);
  }
  decrease(){
    //using .update() method to decrease count value
    this.count.update(value => this.count()-1);
  }
}

HTML Code (component.html)

<div>
   <div class="count">
      <h3>{{count()}}</h3>
   </div>
   <div class="btn">
      <button (click)="increase()">Increase count</button>
	  <button (click)="decrease()">Decrease count</button>
   </div>
</div>

CSS Code (component.css)

.main{
    width: 300px;
}
.count{
    font-size: 40px;
    font-weight: bold;
    font-family: sans-serif;
    text-align: center;
}
button{
    padding: 10px 4px;
    margin: 0px 5px auto;
    background-color: green;
    color: white;
    border-radius: 5px;
    cursor: pointer;
}

Output

Following is the output of the above code:

Writable Signals

Computed Signals

In Angular, the Computed Signals are read-only signals, which means they "can't be edited" by the user. The computed signals drive their values from other signals (e.g., writable signals).

You can define the Computed signal using the computed() function by specifying the derivation.

Syntax

Following is the syntax of the Angular Computed Signal

// Initializing a signal with an initial value
count = signal(initial_value);

// Creating a computed signal
new_count: Signal<number> = computed(() => count() + new_value);

Here,

In the above snippet of code, the new_count signal depends on the count signal, so whenever the count signal updates, angular knows that the new_count also needs to be updated.

Important Points of the Computed Signals

1. Computed signals are both lazily evaluated and memorized

In angular, the Computed (read-only) signals are both lazily evaluated and memorized, so the new_count derivation function does not run to calculate its value until the first time you read the "new_count".

2. Computed signals are not writable signals

The Computed signals are "not writable signals", because you can not directly assign values to them as:

new_count.set(10);

The above snippet of code in the application will produce a compilation error because new_count is "not" a Writable Signal.

Example of Computed Signal in Angular

We will create two writable signals named length and breadth with initial values of 20 and 40. Then we will create a Computed signal named area, which will be the "product" of both writable signal −

TypeScript code (component.ts)

import { Component, computed, signal, WritableSignal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent{
  length: WritableSignal<number> = signal(20);
  breadth: WritableSignal<number> = signal(40);
  area:any = 0;
  calculate(){
    this.area = computed(()=> this.length() * this.breadth());
  }
}

HTML code (component.html)

<div class="main">
  <div class="count">
    <h3>{{area()}}</h3>
  </div>
  <div class="btn">
    <button (click)="calculate()">Calculate area</button>
  </div>
</div>

Output

The output of the above code −

computed signals

Note: When the Writable signals (length and breadth) values will change the Computed signal (area) value will change automatically.

Effects

In Angular, the effects is an "operation" that "runs" when "one or more signal values changed" at a time. The effect always runs at least once.

Similar to the Angular signals, effects keep track of their dependencies dynamically.

Hint: The good place to create effects is within the constructor because the effect function requests an "injection context".

Syntax

Following is the syntax of the Angular effects

effect(() => {
 //statement or expressions....
})

effect accepts a "function", within which will perform all the tasks.

Example of Effect in Angular Signal

We will create "two effects" for count and color signal within the constructor, which will run when one or more signal values change or at least once when the application runs −

TypeScript code (component.ts)

import { Component, effect, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent{
  constructor( ){
    //count signal
    effect(()=>{
      console.log("Effect due to count signal is trigger: " + this.count());
    });

    //color signal
    effect(()=>{
      console.log("Effect due to color signal is trigger: " + this.color());
    });
  }
  
  count = signal(0);
  color = signal(["red", "green"]);

  display(){
    this.count.set(30);
    this.color.update(value => [...value, "Yellow"]);
  }
}

HTML code (component.html)

<div class="main">
  <div class="count">
    <h3>{{count()}}</h3>
  </div>
  <div class="btn">
    <button (click)="display()">Display</button>
  </div>
</div>

Output

When the signal "values are not changed", the effect runs at least once:

effects

When signal "values changed":

effects

Why to use Signals in Angular?

Here are some points that tell about why to use the Signals in Angular project:

  • Automatic updates: Signals are automatically update any dependent computed signals or view when their values changes.
  • Efficient Change Detection: Signals are optimized for minimal change detection cycles, which enhance the application performance.
  • Composable Logic: Signals and computed signals can be used together to build complex reactive logic in a flexible and reusable manner.

Advantages of Signals in Angular

Below is a list of the advantages of the Angular Signals

  • Signals make Angular lighter and point the way to a future without Zone.js (used for change detection). They enable Angular to find out about components that need to be updated directly.
  • We will be notified when the signal value changes, and then do something in response to the new signal value.
  • It is avoiding unnecessary checks of components whose data didn't change.
Advertisements