Angular 17 introduces a powerful new feature called Signals, which provides an efficient and reactive approach to state management in your applications. This blog post will delve into what Signals are and demonstrate their usage with a real-world example: a Todo List application.
What are Signals?
Signals in Angular 17 represent a new reactive state management mechanism. They allow you to create reactive values that automatically update any dependent computations whenever the state changes. This makes it easier to manage UI state and create reactive user interfaces with less boilerplate code and improved performance.
Key Benefits of Signals
- Automatic Updates: Signals propagate changes automatically to any dependent computations, reducing the need for manual state management and ensuring that the UI stays in sync with the state.
- Declarative Syntax: The declarative nature of Signals makes the code easier to read, understand, and maintain.
- Improved Performance: Signals are designed to be efficient, minimizing unnecessary re-renders and computations, which leads to better performance.
In this example, we’ll explore a simple counter component to demonstrate how to use Signals and the computed
function in Angular 17.
import { Component } from '@angular/core';
import { signal, computed } from '@angular/signals';
@Component({
selector: 'app-signal-example',
template: `
<div>
<button (click)="increment()">Increment</button>
<p>Counter: {{ counter() }}</p>
<p>Double: {{ doubleCounter() }}</p>
</div>
`
})
export class SignalExampleComponent {
counter = signal(0);
doubleCounter = computed(() => this.counter() * 2);
increment() {
this.counter.set(this.counter() + 1);
}
}
How it works?
-
Initialization:
- The component initializes the
counter
signal to0
.
- The component initializes the
-
Reactivity:
- When the button is clicked, the
increment
method updates thecounter
signal. - The
doubleCounter
computed value automatically recalculates because it depends oncounter
.
- When the button is clicked, the
-
Automatic Updates:
- The component’s template displays the current values of
counter
anddoubleCounter
. - When
counter
changes, Angular automatically updates the displayed values, ensuring the UI is always in sync with the state.
- The component’s template displays the current values of
Real-World Example: Todo List Application
Let’s build a simple Todo List application to demonstrate the usage of Signals in Angular 17.
Step 1: Setting Up the Angular Project
First, create a new Angular project if you haven’t already:
ng new angular-signals-todo
cd angular-signals-todo
ng serve
Step 2: Creating the Todo Service with Signals
We’ll create a TodoService
to manage our todo list state using Signals.
// src/app/todo.service.ts
import { Injectable, signal } from '@angular/core';
interface Todo {
id: number;
title: string;
completed: boolean;
}
@Injectable({
providedIn: 'root'
})
export class TodoService {
private nextId = 1;
todos = signal<Todo[]>([]);
addTodo(title: string) {
const newTodo: Todo = { id: this.nextId++, title, completed: false };
this.todos.update(todos => [...todos, newTodo]);
}
toggleTodoCompletion(id: number) {
this.todos.update(todos =>
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
}
removeTodo(id: number) {
this.todos.update(todos => todos.filter(todo => todo.id !== id));
}
}
In this service:
- We define a
Todo
interface to structure our todo items. - The
todos
signal is initialized as an empty array. - Methods
addTodo
,toggleTodoCompletion
, andremoveTodo
are provided to manipulate the todos state.
Step 3: Creating the Todo Component
Now, let’s create a component to interact with our TodoService
and display the todo list. We’ll make this a standalone component to take advantage of Angular 17’s standalone components feature.
// src/app/todo.component.ts
import { Component } from '@angular/core';
import { TodoService } from '../todo.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css'],
standalone: true,
providers: [TodoService],
imports: [CommonModule, FormsModule],
})
export class TodoComponent {
newTodoTitle = '';
constructor(public todoService: TodoService) {}
addTodo() {
if (this.newTodoTitle.trim()) {
this.todoService.addTodo(this.newTodoTitle.trim());
this.newTodoTitle = '';
}
}
toggleCompletion(id: number) {
this.todoService.toggleTodoCompletion(id);
}
removeTodo(id: number) {
this.todoService.removeTodo(id);
}
}
Step 4: Creating the Todo Component Template
Next, create a template for the TodoComponent
to display and interact with the todo list.
<!-- src/app/todo.component.html -->
<div class="todo-app">
<h1>Todo List</h1>
<input
type="text"
[(ngModel)]="newTodoTitle"
placeholder="What needs to be done?"
(keyup.enter)="addTodo()"
/>
<button (click)="addTodo()">Add</button>
<ul>
<ul>
@for (todo of todoService.todos();track todo.id) {
<li>
<input
type="checkbox"
[checked]="todo.completed"
(change)="toggleCompletion(todo.id)"
/>
<span [class.completed]="todo.completed">{{ todo.title }}</span>
<button (click)="removeTodo(todo.id)">Remove</button>
</li>
}
</ul>
</ul>
</div>
Step 5: Adding Styles
Add some basic styles for the TodoComponent
.
/* src/app/todo.component.css */
.todo-app {
max-width: 400px;
margin: 0 auto;
text-align: center;
}
.completed {
text-decoration: line-through;
color: grey;
}
Step 6: Adding the Todo Component to the App Module
Register the TodoComponent
in your AppModule
.
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { TodoComponent } from './todo/todo.component';
bootstrapApplication(TodoComponent);
Step 7: Using the Todo Component in the App Component
Use the TodoComponent
in your index.html
.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Angular 17 Signal Demo</title>
<meta charset="UTF-8" />
</head>
<body>
<app-todo></app-todo>
</body>
</html>
Demo
Conclusion
With Angular 17’s Signals, you can create reactive state management with less boilerplate and improved performance. In this example, we demonstrated how to build a Todo List application using Signals to manage the state of the todos. This approach ensures that the UI remains responsive and efficient, automatically updating whenever the state changes. As you explore Angular 17 further, you’ll find that Signals provide a powerful and intuitive way to handle state in your applications.