Friday, 18 April 2025

Angular 19 | Chapter 7 | Tracking Angular Application State with Signals

Tracking Angular web application state with Signals is part of Angular's modern reactive approach — it's super clean, fast, and intuitive. Angular Signals were introduced as a way to make reactivity simpler and more predictable than RxJS alone in certain cases.

 

🧠 What Are Signals in Angular?

Signals are a way to track reactive values and automatically update the UI or other logic when those values change.

They’re similar to BehaviorSubject or computed properties in other frameworks, but:

  • They’re built into Angular.
  • No need to manually subscribe() and unsubscribe().
  • Way less boilerplate than RxJS for basic state tracking.

 

🧩 Basic Signal Concepts

  • signal() – creates a reactive value
  • computed() – creates a derived value based on other signals
  • effect() – runs side effects when signal values change

 

🚀 Example: Managing State with Signals

Let’s say we want to manage a task list in a service:

import { Injectable, signal, computed, effect } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class TaskService {
  private tasks = signal<string[]>([]); //
Reactive signal

// Public read-only signal
  readonly tasks$ = computed(() => this.tasks());

// Add a task
  addTask(task: string) {
    this.tasks.update(current => [...current, task]);
  }

// Remove a task
  removeTask(index: number) {
    this.tasks.update(current => current.filter((_, i) => i !== index));
  }
}

Now in a component:

@Component({
  selector: 'app-task-list',
  standalone: true,
  template: `
    <ul>
      <li *ngFor="let task of taskService.tasks$()">
        {{ task }}
      </li>
    </ul>
  `,
})
export class TaskListComponent {
  constructor(public taskService: TaskService) {}
}

You get reactivity with zero subscriptions

Any changes to tasks will automatically reflect in the template

No need for async pipe, OnPush, or ChangeDetectorRef

 

🛠 Signal Lifecycle Helpers

effect()

Trigger code when a signal changes (side effects):

effect(() => {
  console.log("Tasks updated:", this.tasks());
});

computed()

Create a signal that derives from others:

readonly taskCount = computed(() => this.tasks().length);

 

💡 Why Use Signals for State?

Benefits

Compared to

Zero manual subscriptions

RxJS

Automatic change detection

ChangeDetectorRef, OnPush

Cleaner syntax

BehaviorSubject & .next()

Faster and more predictable

Especially in component templates

 

🧪 When to Use Signals vs RxJS?

Use Signals When...

Use RxJS When...

Local or simple global state

Complex async flows (HTTP, websockets)

You want template reactivity with ease

You need stream operators (switchMap, etc.)

Need minimal boilerplate

Need more stream control

 

🧠 Summary

Concept

Description

signal()

Creates a reactive variable

computed()

Creates a derived value based on signals

effect()

Reacts to signal changes (side effects)

update()

Updates signal values cleanly

 

No comments:

Post a Comment