Angular Signals are a modern reactive primitive introduced to make state management more predictable and easier to work with. They simplify handling reactive data in Angular applications by providing clear and concise APIs. This blog will cover what signals are, their types (like writable and computed signals), and how to use effects with signals. Examples are included to help you grasp these concepts effectively.
What Are Signals in Angular?
A Signal in Angular is a mechanism for managing reactive state. Unlike traditional approaches like Observables or EventEmitters, signals offer a straightforward way to declare and update reactive variables while ensuring automatic updates in the view when the state changes.
Key Features of Signals:
- Synchronous updates: Changes to signals propagate immediately.
- Trackability: Angular automatically tracks dependencies on signals within components and directives.
- Ease of Use: Signals simplify the mental model for handling state compared to Observables.
Writable Signals
A Writable Signal is the most basic type of signal that you can define and update. It represents a reactive piece of state that can be changed over time.
Defining a Writable Signal
To define a writable signal, use the signal() function provided by Angular:
import { signal } from '@angular/core';
export class CounterComponent {
// Declare a writable signal
counter = signal(0);
increment() {
this.counter.update(value => value + 1);
}
decrement() {
this.counter.update(value => value - 1);
}
}
Methods for Writable Signals
- set(): Sets a new value.
this.counter.set(10); // Set counter to 10
- update(): Updates the value using a callback.
this.counter.update(value => value * 2); // Double the current counter value
3.mutate(): Mutates the value directly for objects or arrays.
this.arraySignal.mutate(array => array.push(5));
Computed Signals
A Computed Signal derives its value from one or more other signals. It is automatically updated whenever its dependencies change.
Defining a Computed Signal
To create a computed signal, use the computed() function:
import { signal, computed } from '@angular/core';
export class PriceCalculator {
price = signal(100);
quantity = signal(2);
// Define a computed signal
totalPrice = computed(() => this.price() * this.quantity());
}
Usage
Computed signals are read-only and cannot be directly updated. Instead, you update the signals they depend on (e.g., price or quantity), and the computed signal (totalPrice) will automatically recalculate.
Effects
Effects are used to perform side effects whenever signals change. For example, you might use effects to log changes, fetch data, or interact with external APIs.
Creating an Effect
Use the effect() function to set up an effect:
import { signal, effect } from '@angular/core';
export class EffectExample {
counter = signal(0);
constructor() {
// Log the counter value whenever it changes
effect(() => {
console.log('Counter value:', this.counter());
});
}
increment() {
this.counter.update(value => value + 1);
}
}
Use Cases for Effects
- Logging changes to the console.
- Making HTTP calls based on state changes.
- Updating DOM elements outside Angular’s control.
Example: A Complete Application with Signals
Here’s a simple counter app demonstrating writable signals, computed signals, and effects:
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<div>
<p>Counter: {{ counter() }}</p>
<p>Double: {{ doubleCounter() }}</p>
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>
</div>
`
})
export class CounterComponent {
counter = signal(0);
doubleCounter = computed(() => this.counter() * 2);
constructor() {
effect(() => {
console.log('Counter changed:', this.counter());
});
}
increment() {
this.counter.update(value => value + 1);
}
decrement() {
this.counter.update(value => value - 1);
}
}
Best Practices for Using Signals
- Minimize Effects: Keep side effects lightweight and purposeful.
- Prefer Computed Signals for Derived State: Use computed() for any value that depends on other signals.
- Avoid Over-Mutation: Use update() instead of mutate() when possible to ensure clear intent.
- Combine Signals with Angular’s Dependency Injection: Use signals alongside services for scalable state management.
Conclusion
Angular Signals offer a new way to handle reactive state in Angular applications. With their simplicity and powerful features like writable signals, computed signals, and effects, they can significantly improve the developer experience and application performance. By understanding and adopting signals, you can write more predictable and maintainable Angular code.
Author Of article : Sangeeth p Read full article