Telerik blogs

Angular’s Signals API is a powerful reactivity-focused tool for managing state. In this article, we’ll learn how to use signals, computed signals and effects, as well as experimental features like linked signals and resources introduced in Angular 19.

Angular, a robust framework for building dynamic and scalable web applications, provides developers powerful tools for creating efficient and high-performing applications. Among its many capabilities is the Signals API, a helpful approach to managing reactive state. In this article, we’ll explore what signals are, how they work and some of the recent updates introduced in Angular 19. Let’s dive in!

Signals

At its core, a signal is a lightweight wrapper around a value that notifies its consumers when it changes. Signals introduce a declarative, reactivity-focused approach to managing state. Unlike other framework state management solutions, signals explicitly track dependencies, making our code more predictable and easier to debug.

Signals are ideal for managing local state within components, building computed values from other state, and executing side effects when state changes. They offer three primary constructs—signal, computed and effect. Let’s look at some code to better understand how these constructs work.

Using Signals in Components

Signals hold a value that can be updated and read reactively. To create a signal, we can import the signal function from @angular/core and use the signal() function to define a signal that holds a value. We can then access the signal’s value in the template by calling the signal function, and use the update() method to change the value reactively.

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <div>
      <p>Counter: {{ count() }}</p>
      <button (click)="increment()">Increment</button>
    </div>
  `,
})
export class CounterComponent {
  // Create a signal with an initial value of 0
  count = signal(0);

  // Function to increment the count value
  increment() {
    this.count.update(value => value + 1);
  }
}

In the above example:

  • The count signal is initialized with the value 0.
  • The increment() function updates the value of count by adding 1 to the previous value.
  • The template displays the current count value and includes a button to trigger the increment() function.

Computed Signals

Computed signals allow us to create a new signal whose value is derived from other signals. This helps in scenarios where we need a value that depends on the state of multiple signals. To create a computed signal, we import the computed function from @angular/core and use the computed() function to define a signal that derives its value from other signals.

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

@Component({
  selector: 'app-double-counter',
  template: `
    <p>Double Counter: {{ doubleCount() }}</p>
    <button (click)="increment()">Increment</button>
  `,
})
export class DoubleCounterComponent {
  // Create a base signal
  count = signal(0);

  // Create a computed signal that is double the value of count
  doubleCount = computed(() => this.count() * 2);

  // Function to increment the count value
  increment() {
    this.count.update(value => value + 1);
  }
}

In this example:

  • The doubleCount signal is computed based on the value of count.
  • Whenever count changes, doubleCount is automatically recalculated. This makes it easy to manage derived state without writing extra logic to manually update values.

Effects

Effects are used to run some code whenever a signal’s value changes. This is useful for side effects such as logging or updating external data.

To create an effect, we import the effect function from @angular/core and use the effect() function to define code that runs whenever a signal’s value changes.

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

@Component({
  selector: 'app-logger',
  template: `
    <button (click)="increment()">Increment</button>
  `,
})
export class LoggerComponent {
  // Create a signal with an initial value of 0
  count = signal(0);

  constructor() {
    // Define an effect to log the value of count whenever it changes
    effect(() => {
      console.log(`The count is: ${this.count()}`);
    });
  }

  // Function to increment the count value
  increment() {
    this.count.update(value => value + 1);
  }
}

In this example:

  • The effect function logs the value of count to the console whenever it changes.
  • Effects are useful for any code that needs to react to changes in the signal, such as updating the DOM manually or triggering network requests.

The primary signals in Angular are often signal, computed and effect, which are used to create reactive state, derive values and manage side effects, respectively. However, other types of signals also play an important role in building reactive applications. These include but are not limited to input signals, output signals and view queries, each serving a distinct purpose in enhancing reactivity and component interaction. As of Angular v19, several of these signal types have now been moved to stable and we’ll spend some time in a follow-up article discussing these.

With Angular v19, the Signals API introduces two new experimental features currently in developer preview: linked signals and resources.

Linked Signals [Experimental]

A linked signal is a writable signal that depends on another reactive state. Often, in UIs, mutable state is needed that still tracks some higher-level state, such as in a selection UI where the “current selection” needs to be reset if the list of options changes. The linkedSignal primitive captures this kind of dependency effectively.

import { Component, signal, linkedSignal } from '@angular/core';

@Component({
  selector: 'app-linked-signal',
  template: `
    <p>Options: {{ options() }}</p>
    <p>Current Choice: {{ choice() }}</p>
    <button (click)="updateOptions()">Update Options</button>
  `,
})
export class LinkedSignalComponent {
  options = signal(['apple', 'banana', 'fig']);

  // Choice defaults to the first option but can be changed by the user.
  choice = linkedSignal(() => this.options()[0]);

  // Function to update the options
  updateOptions() {
    this.options.set(['peach', 'kiwi']);
  }
}

In the above example:

  • The options signal holds an array of items.
  • The choice signal is linked to the first item in the options list by default.
  • When options are updated, choice automatically resets to the new first item, demonstrating the reactive relationship between the signals.

The linkedSignal primitive expresses the relationship between options and choice clearly, without the need for an effect to manage this dependency manually.

Resources [Experimental]

The resource API is an experimental feature in Angular 19 that integrates signals with asynchronous data handling. It simplifies working with async operations while keeping Angular’s reactive architecture.

A resource consists of a request function that defines what data to fetch based on signals, a loader that performs the asynchronous task when the request changes, and a resource instance that exposes signals for the value, loading state and error state, allowing the application to respond reactively to the data’s status.

Here is a code example:

import { Component, signal, resource } from '@angular/core';

export class ResourceComponent {
  query = signal('initial query');
  data = resource({
    request: this.query,
    loader: async ({ request }) => {
      const response = await fetch(`https://api.example.com/data?q=${request}`);
      return response.json();
    },
  });

  // ...
}

The resource API fetches data based on the query signal in the above example. The newly created data resource reflects the different states of the asynchronous operation, including loading, error and the actual fetched data. When the query value changes, the resource automatically triggers a new request, keeping the component in sync with the updated data.

The APIs for linked signals and resources are still in an experimental stage, and Angular is seeking developer feedback to refine them further. If you’re interested in trying out linked signals and resources, check out the following official documentation: dependent state with linkedSignal and async reactivity with resources.

Wrap-up

Angular’s Signals API provides a powerful, declarative way to manage state and reactivity. Developers can control state flow in their applications using signals, computed signals and effects. Angular 19 builds on this with features like input and output signals, view queries and the new linked signals and resource API, further enhancing Angular’s reactive capabilities.

For more information on Angular signals and the new features introduced in Angular 19, be sure to check out the following resources:


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.