DEV Community

Cover image for # Angular Signals Explained Like You're Building Your First App
Olajumoke Ipoola
Olajumoke Ipoola

Posted on

# Angular Signals Explained Like You're Building Your First App

You've probably heard about Signals if you're learning angular. At first, they can seem like just another state management feature, but they're actually one of the best additions to Angular.

In this article, we'll build a simple counter app to learn four core Signal APIs:

  • signal()
  • update()
  • computed()
  • effect()

What is a Signal?

Think of a Signal as a reactive variable that Angular keeps an eye on.

With a normal variable, changing its value doesn't automatically tell Angular that something has changed.

A Signal is different. Angular knows exactly what has to be updated on the screen if its value changes.

Creating Your First Signal

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

count = signal(0);
Enter fullscreen mode Exit fullscreen mode

Unlike a normal variable, you read a Signal by calling it like a function.

<h2>{{ count() }}</h2>
Enter fullscreen mode Exit fullscreen mode

Notice the () at the end, that's how you access its current value.

Updating a Signal

There are two common ways to update a Signal.

Use set() when you want to replace the value completely.

count.set(10);
Enter fullscreen mode Exit fullscreen mode

Use update() when the new value depends on the current one.

count.update(value => value + 1);
Enter fullscreen mode Exit fullscreen mode

Let's add increment and decrement buttons.

increment() {
  this.count.update(value => value + 1);
}

decrement() {
  this.count.update(value => value - 1);
}
Enter fullscreen mode Exit fullscreen mode
<button (click)="decrement()">-</button>

<span>{{ count() }}</span>

<button (click)="increment()">+</button>
Enter fullscreen mode Exit fullscreen mode

Every click updates the Signal, and Angular refreshes the UI automatically.

Deriving Values with computed()

Imagine you want to display whether the counter is even or odd.

Instead of calculating it every time, use computed().

import { computed } from '@angular/core';

isEven = computed(() => this.count() % 2 === 0);
Enter fullscreen mode Exit fullscreen mode

Then display it in the template.

<p>{{ isEven() ? 'Even' : 'Odd' }}</p>
Enter fullscreen mode Exit fullscreen mode

Whenever count changes, isEven updates automatically.

Use computed() whenever one value depends on another.

Reacting to Changes with effect()

Sometimes you don't want to calculate a value—you want to perform an action whenever a Signal changes.

That's what effect() is for.

import { effect } from '@angular/core';

constructor() {
  effect(() => {
    console.log('Count is', this.count());
  });
}
Enter fullscreen mode Exit fullscreen mode

Every time the counter changes, Angular runs the effect again.
Avoid using effect() to calculate values. That's what computed() is designed for.


Putting It All Together

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

@Component({
  selector: 'app-counter',
  templateUrl: './counter.component.html'
})
export class CounterComponent {
  count = signal(0);

  isEven = computed(() => this.count() % 2 === 0);

  constructor() {
    effect(() => {
      console.log('Count changed:', this.count());
    });
  }

  increment() {
    this.count.update(value => value + 1);
  }

  decrement() {
    this.count.update(value => value - 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

Signals make Angular state management much easier to understand. If you're just getting started, remember this simple rule:

  • Store state with signal()
  • Change state with set() or update()
  • Derive state with computed()
  • React to changes with effect()

I hope this helps.

Top comments (0)