How to update an observable when it has been converted to Signal if new data has been emitted?

520 views Asked by At

I am trying to utilse some of Angular's new reactivity with Signal. I am following this workflow, and converting one of my observables into a signal, this is then being read and displayed on my screen.

https://angular.dev/guide/signals/rxjs-interop#tosignal

Working Example

** T.S **

    response_obs$ =
            this.apiService.getResponse();

    response_signal:  Signal<any> = toSignal(response_obs$,  {initialValue: 0})


** HTML **

    <div *ngIf="response_signal()">
      <div class="item" *ngFor="let item of response_signal().items ">
          <label>{{ item.name }}</label>
          <small>{{ item.website }}</small>
      </div>
    </div>

This all works and prints correctly. We have replaced the async pipe by using the toSignal() function.

What I am trying to achevive next is reacitvity. When I post to this API in aother part of the application the observable will have new data to emit, if I refresh the webpage this appears but the change is not registered.

The update to the observable can be triggered via and Event Emitter, is there tooling within Angular Signals workflow that can be used here?

2

There are 2 answers

0
Danilo Verde On

Angular's Signals library, introduced in Angular 13, provides a way to work with reactive data in a more concise and efficient manner. However, for your specific use case of updating the Signal when an observable changes due to an event emitter, you can achieve reactivity using Angular Signals. To make your Signal react to changes in the underlying observable triggered by an event emitter, you can use the merge operator from RxJS to combine the original observable with an observable that represents the event emitter. Here's how you can do it: 1. Define your response_obs$ observable as you did before:

response_obs$ = this.apiService.getResponse();
  1. Create an observable for the event emitter. You can do this by creating a subject and emitting values when the event occurs:

    private updateEventEmitter = new Subject();

// Method to trigger the update event

triggerUpdate() {
  this.updateEventEmitter.next();
}

// Subscribe to the event emitter observable

updateEvent$ = this.updateEventEmitter.asObservable();
  1. Combine your original response_obs$ observable and the updateEvent$ observable using the merge operator. This will create a new observable that emits whenever either the original observable emits data or the event emitter emits a value.

    combinedObs$ = merge( this.response_obs$, this.updateEvent$.pipe(switchMap(() => this.apiService.getResponse())) );

  2. Convert the combinedObs$ observable into a Signal, just like you did before:

    response_signal: Signal = toSignal(this.combinedObs$, { initialValue: 0 });

Now, whenever you call triggerUpdate(), it will trigger the event emitter, which in turn will update the combinedObs$ observable with new data from this.apiService.getResponse(). The Signal will react to changes in combinedObs$ and display the updated data in your HTML template. In your HTML template, you don't need to make any changes because you're already using the response_signal() function to display the data, and it will automatically react to changes in the Signal.

0
Daniel Gimenez On

Create an Observable to call getResponse() every time a response changes. Initiating it with a BehaviorSubject makes the most sense so that it will immediately start an observable stream. The create a stream inside of toSignal() (you don't even have to assign it to a variable) that chains emissions to calls to the service.

TS

responseChanged = new BehaviorSubject<void>(undefined);

response_signal = toSignal(
  this.responseChanged.pipe(
    switchMap(() => this.apiService.getResponse())
  ),  
  { initialValue: 0 }
);

HTML

<app-something (onChange)="responseChanged.next()" />