Convertir observables en señales en angular: lo que necesita saber

May 01 2023
Angular v16 viene con un nuevo paquete llamado rxjs-interop, que presenta la función toSignal que convierte un observable en una señal. En este artículo, veremos más de cerca esta nueva característica y su uso.

Angular v16 viene con un nuevo paquete llamado rxjs-interop, que presenta la toSignalfunción que convierte un observable en una señal. En este artículo, veremos más de cerca esta nueva característica y su uso.

Para comenzar a usar la toSignalfunción, debemos importarla desde el @angular/core/rxjs-interopmódulo. Aquí hay un fragmento de código de ejemplo que demuestra su uso:

import { toSignal } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';

@Component({
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000);
  counter = toSignal(this.counter$);
}

Vale la pena señalar que, a diferencia de la asynctubería, podemos leer el valor de la señal inmediatamente en nuestro componente, lo que puede producir undefined.

Además, la toSignalfunción se suscribe a lo observable inmediatamente , lo que puede provocar resultados no deseados en algunos casos si hay efectos secundarios.

Si tenemos un código que usa la asynccanalización con una ngIfdirectiva, se suscribirá al observable solo cuando rendericemos la plantilla.

@Component({
  standalone: true,
  template:`<div *ngIf="someCondition">
     <div *ngFor="let item of source$ | async"></div>
  </div>`,
})
export class FooComponent {
  source$ = inject(Service).someMethod();
}

En caso de que queramos eliminar el undefinedtipo de nuestra señal resultante, tenemos dos opciones. El primero es pasar un valor inicial cuando tenemos un asyncobservable que no se dispara inmediatamente.

@Component({
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000);
  counter = toSignal(this.counter$, { initialValue: 0 });
}

@Component({
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000).pipe(startWith(0));
  counter = toSignal(this.counter$, { requireSync: true });
}

Cuando toSignalse llama a la función, primero verifica para asegurarse de que se está llamando en un contexto de inyección . Si no, se lanzará un error. Eso significa que podemos usar la toSignalfunción solo cuando la inject() función está disponible, excepto en los casos en que usamos la manualCleanupopción o pasamos injectorexplícitamente.

La razón de esto es que Angular cancelará automáticamente la suscripción cuando se destruya el contexto de envoltura. Hace esto usando el nuevo OnDestroygancho que obtiene al usar la inject()función o el proporcionado explícitamente injector:

@Component({
  selector: 'foo',
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000);
  counter: Signal<number | undefined>; 
  private injector = inject(Injector);

  ngOnInit() {
    this.counter = toSignal(this.counter$, { injector: this.injector } );
  }
}

@Component({
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000).pipe(take(3));
  counter = toSignal(this.counter$, { manualCleanup: true });
}

@Component({
  standalone: true,
  template:`{{ counter() }}`,
})
export class FooComponent {
  counter$ = interval(1000);
  counter = toSignal(this.counter$, { initialValue: 0 });

  ngOnInit() {
    try {
      this.counter();
    } catch (e) {
      console.log(e);
    }
  }
}

¡ Sígueme en Medium o Twitter para leer más sobre Angular y JS!