Search code examples
angularsignalsangular-signals

Angular Signals - What is the advantage?


I am trying to understand the advantage of using Angular Singnals. There is a count example given in many explanations but what I am trying to understand is the advantage of using signals this way as opposed to the way I have done below via variables myCount and myCountDouble?

https://stackblitz.com/edit/angular-qtd3ku?file=src/main.ts

Here is my code + stackblitz

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { signal } from './signals/signal';
import { computed } from './signals/computed';
import { effect } from './signals/effect';
import { TestArraysCmp } from './testing-arrays.component';
import { TestObjectsCmp } from './testing-objects.component';

/*
  ⚠️ Please keep in mind that this is only the signals implementation. We still depend on zone and current CD strategy to propagate change in the template which might not be the case when Angular releases signals officially!
*/

// Signals are primitives, so we can use them outside the component class
const myValue = signal(10000);

effect(() => {
  console.log('MyValue changed', myValue());
});

// Uncomment this
// setInterval(() => {
//   myValue.update((s) => s - 1);
// }, 1000);

@Component({
  selector: 'my-app',
  standalone: true,
  template: `
    <div>Count: {{ count() }}</div>
    <div>Double: {{ double() }}</div>
    <div>MyCount: {{myCount}}</div>
    <div>MyDoubleCount: {{myCountDouble}}</div>
    <button (click)="inc()">Increase</button>
    <button (click)="reset()">Reset</button>

    <br>
    <!-- <test-arrays /> -->
    <!-- <test-objects /> -->

  `,
  imports: [TestArraysCmp, TestObjectsCmp],
})
export class App {
  myCount: number = 0;
  myCountDouble: number = 0;
  myCountType: string;

  count = signal(0);

  double = computed(() => this.count() * 2);

  countType = computed(() => (this.count() % 2 === 0 ? 'even' : 'odd'));

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

  inc() {
    this.count.update((c) => c + 1);
    this.myCount = this.myCount + 1;
    this.myCountDouble = this.myCount * 2;
    this.myCountType = this.myCount % 2 === 0 ? 'even' : 'odd';
    console.log('Old Way', this.myCount, 'is', this.myCountType);
  }
  reset() {
    this.count.set(0);
    this.myCount = 0;
    this.myCountDouble = 0;
    this.myCountType = '';
  }
}

bootstrapApplication(App);

Solution

  • The advantage is mostly related to the way Angular handles change detection.

    With Zone.js, when you trigger the inc() method, Angular will look down the entire component tree for changes, even if just that one thing has changed.

    With signals, we indicate specifically that only a particular thing has changed and needs to be updated.

    As updating a counter is a synchronous operation - we don't need to wait for an API response, we know the value, and therefore we can use it to update the counter right away - using a signal will perform better as it indicates to Angular exactly that only the elements that depend of that signal should be updated thus eliminating the need for dirty checking the entire component tree.