Skip to content

GitLab

  • Menu
Projects Groups Snippets
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • A angular
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Metrics
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Infrastructure Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • imunnic
  • angular
  • Wiki
  • Modulo 5

Last edited by imunnic Dec 22, 2025
Page history

Modulo 5

Gestión de Estado en Angular con Signals usando ResourceService

Introducción

En Angular moderno, los Signals permiten implementar un store reactivo de manera nativa, sin librerías externas. Un Signal mantiene un valor interno y notifica automáticamente a todos los componentes y funciones que dependen de él cuando cambia. Esto hace que la gestión de estado sea simple, predecible y eficiente, especialmente para aplicaciones que requieren compartir datos entre múltiples componentes.

La ventaja de Signals frente a otros enfoques es que la reactividad está integrada en el framework, sin necesidad de subscribe(), observables intermedios o memorias que limpiar. Esto permite que los componentes solo consuman datos y reaccionen automáticamente a los cambios del estado.


Conceptos Clave

  1. Signal: Contenedor de un valor reactivo que puede actualizarse mediante .set() o .update().
  2. Computed: Función derivada que calcula valores basados en Signals, actualizándose automáticamente cuando alguno de los Signals que depende cambian.
  3. Reactividad automática: Los componentes que usan Signals se actualizan sin suscripciones explícitas.
  4. Inmutabilidad opcional: Aunque los valores pueden mutar, es recomendable actualizar el Signal creando un nuevo estado para mantener consistencia y evitar efectos secundarios.

Store con Signals y ResourceService

import { Injectable, signal, computed } from '@angular/core';
import { ResourceService } from './resource.service';
import { Resource } from './resource.model';

@Injectable({ providedIn: 'root' })
export class ResourceStore {
  // Estado principal
  private resourcesSignal = signal<Resource[]>([]);

  // Selector derivado: número total de recursos
  readonly resourceCount = computed(() => this.resourcesSignal().length);

  // Selector derivado: filtrar recursos por nombre
  filterResources(name: string) {
    return computed(() => this.resourcesSignal().filter((r) => r.name.includes(name)));
  }

  constructor(private resourceService: ResourceService) {}

  // Cargar recursos desde el servicio
  loadResources() {
    const resources = this.resourceService.get(); // Devuelve Resource[]
    this.resourcesSignal.set(resources);
  }

  // Añadir un recurso
  addResource(resource: Resource) {
    this.resourcesSignal.update((current) => [...current, resource]);
  }

  // Eliminar un recurso por id
  removeResource(id: number) {
    this.resourcesSignal.update((current) => current.filter((r) => r.id !== id));
  }

  // Actualizar un recurso
  updateResource(updated: Resource) {
    this.resourcesSignal.update((current) =>
      current.map((r) => (r.id === updated.id ? updated : r))
    );
  }
}

Componente que usa la store

import { Component, OnInit } from '@angular/core';
import { ResourceStore } from './resource.store';

@Component({
  selector: 'app-resource-list',
  templateUrl: './resource-list.component.html',
})
export class ResourceListComponent implements OnInit {
  newName = '';

  constructor(public store: ResourceStore) {}

  ngOnInit() {
    // Cargar datos iniciales desde ResourceService
    this.store.loadResources();
  }

  add() {
    if (this.newName.trim()) {
      const newResource = { id: Date.now(), name: this.newName };
      this.store.addResource(newResource);
      this.newName = '';
    }
  }

  remove(id: number) {
    this.store.removeResource(id);
  }
}

Template separado: resource-list.component.html

<h2>Recursos ({{ store.resourceCount()() }})</h2>
<ul>
  <li *ngFor="let resource of store.resources()">
    {{ resource.name }}
    <button (click)="remove(resource.id)">Eliminar</button>
  </li>
</ul>

<input [(ngModel)]="newName" placeholder="Nombre del recurso" />
<button (click)="add()">Añadir Recurso</button>

Notas importantes:

  1. store.resources() y store.resourceCount() se llaman como funciones porque son Signals / computed.
  2. La carga de datos se realiza mediante ResourceService.get(), manteniendo la separación entre obtención de datos y gestión del estado.
  3. Toda la lógica de estado (añadir, eliminar, actualizar) permanece centralizada en el store, dejando los componentes limpios y enfocados en la presentación.
  4. La reactividad es automática: al actualizar resourcesSignal, el template se refresca sin necesidad de subscribe().

Selectores Computados

Los computed permiten derivar información del estado principal de manera reactiva:

// Filtrar recursos cuyo nombre contiene "Angular"
readonly angularResources = computed(() =>
  this.resourcesSignal().filter(r => r.name.includes('Angular'))
);

// Contar recursos con un nombre específico
countResourcesByName(name: string) {
  return computed(() => this.resourcesSignal().filter(r => r.name === name).length);
}

Uso en el template:

<div>Total Angular Resources: {{ store.countResourcesByName('Angular')() }}</div>

Efectos y reactividad adicional

Si se desea reaccionar automáticamente a cambios del estado:

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

effect(() => {
  console.log('Número de recursos:', this.store.resourceCount());
});

Cada vez que cambie resourceCount, se ejecutará la función del effect. Es útil para sincronizaciones externas, registros de auditoría o cálculos derivados.


Buenas Prácticas

  1. Mantener el estado inmutable: actualizar arrays y objetos creando nuevas instancias.
  2. Usar computed para todos los valores derivados.
  3. Separar la lógica de estado de los componentes: el store maneja la manipulación de datos y los componentes solo consumen y disparan acciones.
  4. Usar efectos (effect()) únicamente para side effects, nunca para mutar el estado principal.

Clone repository

Índice

  • Inicio
  • Módulo 1. Fundamentos y arranque del proyecto
    • Módulo 1.1 Angular
    • Módulo 1.2 Estructura
    • Módulo 1.2 Arquitectura y conceptos base
  • Módulo 2. Componentes y reutilización
  • Módulo 3. Navegación y estructura de aplicación
  • Módulo 4. Servicios y comunicación con backend
  • Módulo 5. Gestión de estado (Stores)