Maneja el estado de tu Angular app usando Signals

📡 Signals 🚥 son lo último y más llamativo en Angular 16, y a menudo se comparan con los observables o cómo vamos a usar RxJS. En este artículo, te contaré como crear un servicio para almacenar el estado usando Signals, ya que este es uno de los usos más comunes del BehaviorSubject, como se puede ver en algunos de los artículos a continuación:

Angular State Management With BehaviorSubjectAngular service to handle state using BehaviorSubject

Paso 1. Crear el servicio

El objetivo de este servicio es mantener cualquier tipo de estado en cualquier forma y poder usarlo en varios componentes.Además, queremos facilitar la modificación del estado y proporcionar un fácil acceso a cada una de las propiedades.

Comenzaremos definiendo la clase que otros servicios extenderán para definir el estado que desean almacenar. Esta clase utiliza el signal se inicializa con un objeto vacío de tipo T.

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

constructor() {}

}

Ahora, vamos a trabajar en cómo guardamos el estado.

Para ello, tendremos dos métodos que utilizan el método update dentro de la señal.

set se utiliza para establecer una única propiedad del estadosetState para actualizar un estado parcial o completo./**
* Este actualiza una sola propiedad del estado
*
* @param key – el nombre de la propiedad que se va guardar
* @param data – la informació a guardar
*/
public set<K extends keyof T>(key: K, data: T[K]) {
this.state.update((currentValue) => ({ …currentValue, [key]: data }));
}

/**
* Este se utiliza cuando es necesario actualizar
* varias propiedades del estado.
*
* @param partialState – el estado Parcial o multiples propiedades
* a guardar
*/
public setState(partialState: Partial<T>): void {
this.state.update((currentValue) => ({ …currentValue, …partialState }));
}

El siguiente paso es crear una forma sencilla de leer el estado y hasta solo una propiedad en específico . Para lograr esto, utilizaremos computed ya que genera un nuevo valor reactivo a partir de una expresión:

/**
* Crea una señal con la propiedad a leer del estado
*
* @param key – el nombre de la propiedad que se va a leer
*/
public select<K extends keyof T>(key: K): Signal<T[K]> {
return computed(() => this.state()[key]);
}

Aquí está la implementación completa:

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

constructor() {}

/**
* Crea una señal con la propiedad a leer del estado
*
* @param key – el nombre de la propiedad que se va a leer
*/
public select<K extends keyof T>(key: K): Signal<T[K]> {
return computed(() => this.state()[key]);
}

/**
* Este actualiza una sola propiedad del estado
*
* @param key – el nombre de la propiedad que se va guardar
* @param data – la informació a guardar
*/
public set<K extends keyof T>(key: K, data: T[K]) {
this.state.update((currentValue) => ({ …currentValue, [key]: data }));
}

/**
* Este se utiliza cuando es necesario actualizar
* varias propiedades del estado.
*
* @param partialState – el estado Parcial o multiples propiedades
* a guardar
*/
public setState(partialState: Partial<T>): void {
this.state.update((currentValue) => ({ …currentValue, …partialState }));
}
}

Ejemplo de como se usa el servicio

En la aplicación de ejemplo, tenemos un componente que comparte estado con otros componentes “hijos”, y queremos que al realizar modificaciones en uno de los componentes secundarios se puedan ver los cambios en todos los demás componentes.

Ejemplo de una aplicación con varios componentes que van a actualizar y leer estado

Para lograr esto, crearemos un nuevo servicio que extienda SignalsSimpleStoreService y pasaremos UserState como el tipo:

export interface UserState {
name: string;
company: string;
address: string;
}

@Injectable()
export class UserSignalsStateService extends SignalsSimpleStoreService<UserState> {
constructor() {
super();
}
}

Puedes haber notado que el servicio no tiene providedIn: ‘root’; esto se debe a que planeamos establecerlo como provider en el componente principal, restringiendo el acceso al estado compartido solo a este componente y los otros componentes usados dentro de el.

@Component({
selector: ‘demos-signals-simple-store’,
standalone: true,
imports: [
CommonModule,
PageContentComponent,
ChangeNameComponent,
ChangeCompanyComponent,
ChangeAddressComponent,
],
templateUrl: ‘./signals-simple-store.component.html’,
styleUrls: [‘./signals-simple-store.component.scss’],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [UserSignalsStateService], // 👈 Aqui lo inyectamos
})

Ahora, para leer del estamos, podemos leer un solo atributo utilizando el método select:

@Component({

providers: [UserSignalsStateService],
})
export class SignalsSimpleStoreComponent implements OnInit {

// 👇 Leyendo todo el estado
readonly user = this.userSignal.state.asReadonly();

constructor(private userSignal: UserSignalsStateService) {}
}

Para renderizar los valores, se hace de la forma habitual con las señales:

<div>
<p class=”text-2xl”>Current State</p>
</div>
<div class=”flex-col mt-5″>
<p class=”text-indigo-600 font-semibold”>
Name: <span>{{ user().name }}</span>
</p>
<p class=”text-indigo-600 font-semibold”>
Company:<span>{{ user().company }}</span>
</p>
<p class=”text-indigo-600 font-semibold”>
Address: <span>{{ user().address }}</span>
</p>
</div>
</div>

Finalmente, para actualizar el estado, podemos usar setState para actualizar una parte parcial del estado o usar set para modificar solo una propiedad.

changeName() {
const newName = faker.name.fullName();

// 👇 Actualiza el estado
this.userState.setState({ name: newName } as UserState);
}

changeAddress() {
const newAddress = faker.address.streetAddress(true);

// 👇 Actualiza solo una propiedad
this.userState.set(‘address’, newAddress);
}

Conclusión

Espero que esto te ayude a identificar otros casos de uso para las señales y cómo podemos crear un servicio para mantenerlas. Siempre puedes usar NgRx Component Store para compartir estados entre componentes, pero tal vez quieras crear tu propio servicio para adaptarlo a tus reglas de negocio o estandarizarlo.

Puedes encontrar el código de este artículo aquí:

ng-demos/signals-simple-store.service.ts at main · alfredoperez/ng-demos

Me puedes encontrar en Twitter o en mi website.

Maneja el estado de tu Angular app usando Signals was originally published in ngconf on Medium, where people are continuing the conversation by highlighting and responding to this story.

Leave a Comment

Your email address will not be published. Required fields are marked *