Conversión de Observables a Signals en Angular con toSignal

Clase 16 de 36Curso de Angular Avanzado

Resumen

La integración de RxJS con Angular Signals representa un avance significativo en el manejo de datos reactivos en aplicaciones Angular. Esta combinación permite aprovechar lo mejor de ambos mundos: la potencia de los observables de RxJS y la simplicidad y rendimiento de las signals. Veamos cómo implementar esta integración de manera efectiva en proyectos reales.

¿Cómo convertir observables a signals con toSignal?

La función toSignal nos permite transformar observables de RxJS en signals de Angular, simplificando enormemente nuestro código. Esta función es especialmente útil cuando trabajamos con servicios que utilizan HttpClient, ya que estos generalmente devuelven observables.

Para implementar esta conversión, necesitamos seguir estos pasos:

  1. Importar la función toSignal desde @angular/core.
  2. Aplicar esta función al observable que queremos convertir.
  3. Proporcionar un valor inicial si el observable no tiene uno.

Veamos un ejemplo práctico:

// Antes
export class ListProductComponent implements OnInit {
  private productService = inject(ProductService);
  
  categories = signal<Category[]>([]);
  
  ngOnInit() {
    this.productService.getAll().subscribe(categories => {
      this.categories.set(categories);
    });
  }
}

// Después
export class ListProductComponent {
  private productService = inject(ProductService);
  
  categories = toSignal(this.productService.getAll(), { initialValue: [] });
}

Como podemos observar, el código se reduce significativamente. Ya no necesitamos:

  • Implementar la interfaz OnInit
  • Definir el método ngOnInit()
  • Manejar manualmente la suscripción
  • Llamar al método set() del signal

Todo esto lo hace automáticamente toSignal, lo que resulta en un código más limpio y declarativo.

¿Cuándo usar el valor inicial en toSignal?

Es importante proporcionar un valor inicial cuando trabajamos con observables que no emiten inmediatamente, como es el caso de las peticiones HTTP. En nuestro ejemplo, utilizamos un array vacío como valor inicial:

categories = toSignal(this.productService.getAll(), { initialValue: [] });

Este valor se utilizará mientras se resuelve la petición HTTP, evitando errores de renderizado cuando intentamos acceder a propiedades de un valor que aún no existe.

Limitaciones al usar toSignal

Aunque toSignal simplifica enormemente nuestro código, presenta algunas limitaciones importantes que debemos considerar:

No se puede modificar manualmente el valor

Una restricción significativa es que no podemos usar el método set() en un signal creado con toSignal. Por ejemplo:

// Esto NO funcionará
categories = toSignal(this.productService.getAll(), { initialValue: [] });
categories.set([]); // Error: Property 'set' does not exist

Esto puede ser problemático si necesitamos reiniciar o modificar manualmente el valor del signal en algún momento.

Falta de estados derivados

Otra limitación es que toSignal no proporciona estados derivados como "loading" o "error". Si necesitamos estos estados, tendríamos que crear signals adicionales:

loadingProducts = signal(false);

ngOnInit() {
  this.loadingProducts.set(true);
  this.productService.getAll().subscribe({
    next: (products) => {
      this.products.set(products);
      this.loadingProducts.set(false);
    },
    error: () => this.loadingProducts.set(false)
  });
}

Esto puede llevar a un código más verboso y difícil de mantener, especialmente cuando tenemos múltiples peticiones HTTP.

Buenas prácticas al trabajar con toSignal

Para aprovechar al máximo esta función, considera las siguientes recomendaciones:

  1. Mantén el orden correcto de las inyecciones: Coloca siempre las inyecciones de dependencias al principio de la clase, antes de declarar los signals.
export class ListProductComponent {
  private productService = inject(ProductService); // Primero las inyecciones
  
  categories = toSignal(this.productService.getAll(), { initialValue: [] }); // Luego los signals
}
  1. Proporciona siempre un valor inicial adecuado: Elige un valor inicial que sea compatible con la estructura de datos esperada para evitar errores en el template.

  2. Considera las limitaciones: Si necesitas modificar manualmente el valor o manejar estados como loading o error, evalúa si toSignal es la mejor opción o si deberías usar un enfoque más tradicional.

La función toSignal representa un puente elegante entre el mundo de RxJS y las nuevas signals de Angular, permitiéndonos aprovechar la infraestructura existente basada en observables mientras adoptamos el nuevo paradigma de signals. Sin embargo, es importante entender sus limitaciones para decidir cuándo utilizarla.

En la próxima clase, exploraremos soluciones a estas limitaciones para crear una experiencia más completa al trabajar con datos asincrónicos en Angular.

¿Has experimentado con la conversión de observables a signals en tus proyectos? Comparte tus experiencias en los comentarios.