Search code examples
angularrxjssignals

Angular Signals: How to handle requests to API


So I am new to signals and I am trying to use them more and more in our application. The part where I still cannot wrap my head around is the connection between rxJS and Signals. Because we are using angulars HTTP client, we have to deal with observables (which is not a bad thing). But now I am trying to make my code more reactive by updating a signals value by doing an API call whenever the ID of the scoped object(which is a signal itself) changes. This is what I came up with:

 effect(() => {
      this.apiService.hasSomething({
        customerId: mySignalWhoseValueChanges()
      }).subscribe(value => this.mySignal.set(value));
    });

If i use mySignal() in my template and change mySignalWhoseValueChanges(), it will call the effect() because effect() listens for any signal changes that are used within it.

The thing is, I am 100% sure there is a better approach without using effect(), but I just do not know. I tried using toSignal() but this does not work as its not actually a signal thats referenced, but only the value at the time of creating it, or am I missing something:

this.mySignal = toSignal(this.apiService.hasSomething({
      customerId: mySignalWhoseValueChanges()
    }))

Thanks in advance!


Solution

  • You can combine a signal with an http request pretty easily.

    You can use the toObservable function to watch for changes in/on the signal, then trigger your http request when the id changes.

    Note: Signals are NOT meant to be a replacement for RxJS (at least not anytime soon). They are meant to work in unison.

    To this this, you would take the following steps:

    1. Create a user signal to watch for the current user. (this can be anything I chose a number).
    2. Create a response signal to watch for the http response.
    3. Next create a way to update the user (I chose a form input) that binds to the user signal.
    4. Use toObservable and pass your user signal as the parameter.
    5. When it changes make your http request to the server.
    6. When the server responds, update your response signal with the data.

    StackBlitz Example

    import { toObservable } from '@angular/core/rxjs-interop';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [HttpClientModule, FormsModule, AsyncPipe, JsonPipe],
      styles: ['div{font-family: monospace}'],
      template: `
        @if ({request: page$ | async}) {
          <div>{{response() | json}}</div>
        }
        <p>Enter a number from 1 to 4.</p>
        <input [(ngModel)]="user" />
      `,
    })
    export class App {
      user = signal<number>(0);          // A way to watch users
      response = signal<any>(undefined); // A way to watch for http changes
    
      constructor(private readonly http: HttpClient) {}
    
     
      page$ = toObservable<number>(this.user).pipe( // Watch for user changes
        filter((id) => id > 0),                     // Only make http request for users larger than 0
        tap((id) => console.log('user id', id)),    // Just some debugging
        exhaustMap((id) =>                          // Don't execute the http request if one is already in progress
          this.http
            .get<Response>(`https://example.com/user/${id}`) // Make the http request
            .pipe(tap((response) => this.response.set(response)))   // Update the response
        )
      );
    }