How to Create a State Manager in Angular Using Signals

If you’re looking to create a state manager in Angular, signals are a great way to track changes to data in your application and trigger the re-rendering of components only when necessary. In this article, we’ll show you how to create a simple state manager using signals in Angular.

First, let’s create a service to store state with signals. We can use the SignalsSimpleStoreService class for this purpose. This class uses the signal state, and it is initialized with an empty object of type T. You can then define the state you want to store by extending this class. Here is an example implementation:

import { signal, Signal, computed } from 'signals';

export class SignalsSimpleStoreService<T> {
  readonly state = signal({} as T);

  constructor() {}

  public set<K extends keyof T>(key: K, data: T[K]) {
    this.state.update((currentValue) => ({ ...currentValue, [key]: data }));
  }

  public setState(partialState: Partial<T>): void {
    this.state.update((currentValue) => ({ ...currentValue, ...partialState }));
  }

  public select<K extends keyof T>(key: K): Signal<T[K]> {
    return computed(() => this.state()[key]);
  }
}

You can then use the set method to set a new value for a property, the setState method to update multiple properties in the store, and the select method to retrieve a reactive value for a specific part of the state.

Here are some examples of how you can use the SignalsSimpleStoreService to manage the state in your Angular application:

import { Component } from '@angular/core';
import { SignalsSimpleStoreService } from './signals-simple-store.service';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h1>State Management with Signals</h1>
      <p>Current count: {{ count$ | async }}</p>
      <button (click)="increment()">Increment</button>
      <button (click)="decrement()">Decrement</button>
    </div>
  `,
})
export class AppComponent {
  count$ = this.store.select('count');

  constructor(private store: SignalsSimpleStoreService<{ count: number }>) {}

  increment() {
    this.store.set('count', this.count$ + 1);
  }

  decrement() {
    this.store.set('count', this.count$ - 1);
  }
}

In this example, we create a simple counter application that uses the SignalsSimpleStoreService to manage state. We define a count$ observable that retrieves the current count from the store using the select method. We then use this observable to display the current count in the template.

The increment and decrement methods update the count in the store using the set method. This triggers the recalculation of the count$ observable, which in turn updates the template.

Here’s another example that demonstrates how to use the SignalsSimpleStoreService to manage a list of items:

import { Component } from '@angular/core';
import { SignalsSimpleStoreService } from './signals-simple-store.service';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h1>State Management with Signals</h1>
      <ul>
        <li *ngFor="let item of items$ | async">{{ item }}</li>
      </ul>
      <input [(ngModel)]="newItem" />
      <button (click)="addItem()">Add Item</button>
    </div>
  `,
})
export class AppComponent {
  items$ = this.store.select('items');
  newItem = '';

  constructor(private store: SignalsSimpleStoreService<{ items: string[] }>) {}

  addItem() {
    this.store.setState({ items: [...this.items$, this.newItem] });
    this.newItem = '';
  }
}

In this example, we create a simple application that allows the user to add items to a list. We define an items$ observable that retrieves the current list of items from the store using the select method. We then use this observable to display the list of items in the template.

The addItem method updates the list of items in the store using the setState method. This triggers the recalculation of the items$ observable, which in turn updates the template.

In this article, we’ve shown you how to create a simple state manager using signals in Angular. With this approach, you can easily track changes to data in your application and trigger the re-rendering of components only when necessary. We hope you found this article helpful!

Leave a Comment