← Back to Chapters

Data Types with Signals

⚡ Data Types with Signals

? Quick Overview

Angular’s signals provide a reactive way to manage state. Signals can store and reactively update values of different data types including primitives, objects, and arrays. This makes them highly flexible for managing application state in a predictable way.

? Key Concepts

  • Signals can hold primitive values such as number and string.
  • Signals can also store objects and arrays, making them ideal for structured data.
  • You read a signal’s value by calling it like a function, e.g. count().
  • You update a signal with .set() or .update() to trigger reactivity.
  • For objects and arrays, you typically create a new object/array (using the spread operator) so Angular can detect the change.

? Syntax & Theory

A signal is created using the signal<T>() function, where T is the data type it will hold.

  • signal<number>(0) → a numeric signal starting at 0.
  • signal<string>('John Doe') → a string signal for a username.
  • signal<{ name: string; age: number }>({...}) → an object signal.
  • signal<string[]>([...]) → an array signal.

When you change the signal’s value with .set() or .update(), Angular automatically re-renders any template bindings that depend on that signal.

? Code Examples

1️⃣ Signals with Primitive Data Types

? View TypeScript Code (Primitives)
// Primitive signals for count and username
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  count = signal<number>(0);
  username = signal<string>('John Doe');
}
? View Template Code (Primitives)
<!-- Bind to primitive signals in the template -->
<p>Count: {{ count() }}</p>
<button (click)="count.set(count() + 1)">Increment</button>

<p>Hello, {{ username() }}!</p>

2️⃣ Signals with Objects

? View TypeScript Code (Object Signal)
// Store a user object inside a signal
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  user = signal<{ name: string; age: number }>({
    name: 'Alice',
    age: 25
  });
}
? View Template Code (Object Signal)
<!-- Read and update properties from the user signal -->
<p>Name: {{ user().name }}</p>
<p>Age: {{ user().age }}</p>
<button (click)="user.set({ ...user(), age: user().age + 1 })">Increase Age</button>

3️⃣ Signals with Arrays

? View TypeScript Code (Array Signal)
// Manage a list of tasks using an array signal
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  tasks = signal<string[]>(['Learn Angular', 'Build Project']);
}
? View Template Code (Array Signal)
<!-- Loop over the tasks signal and render each item -->
<ul>
  @for (task of tasks(); let i = $index) {
    <li>{{ i + 1 }}. {{ task }}</li>
  }
</ul>

<button (click)="tasks.set([...tasks(), 'New Task'])">Add Task</button>

? Live Output / Explanation

How the UI reacts to signal changes

  • When you click Increment, the count signal’s value increases and the template automatically shows the new number.
  • When you update the user signal with a new object (using the spread operator), the displayed name and age refresh instantly.
  • When you click Add Task, a new array is created with the extra item, and the list in the template re-renders with the new task.

No manual change detection calls are needed: the signal system re-runs any computations or template bindings that depend on the changed signals.

✅ Tips & Best Practices

  • Always use .set() or .update() methods to modify signals instead of mutating the value directly.
  • Use readonly signals for values that should not change, to keep your state predictable and safe.
  • Use objects and arrays in signals for managing structured and dynamic data such as user profiles or lists.
  • Prefer creating new objects/arrays (e.g., with the spread operator) to ensure changes are detected.

? Try It Yourself / Practice Tasks

  • Create a signal for a counter and build increment/decrement buttons.
  • Store user profile information (name, email, age) in a signal object and update it through a form.
  • Build a to-do list app using an array signal, allowing tasks to be added and displayed dynamically.
  • Add a button that clears the task list by calling tasks.set([]) and observe how the UI updates.