Martin Hochmair
Martin Hochmair

Reputation: 323

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!

Upvotes: 7

Views: 10423

Answers (2)

Get Off My Lawn
Get Off My Lawn

Reputation: 36341

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 do 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
    )
  );
}

Upvotes: 8

danishjoseph
danishjoseph

Reputation: 98

Sharing much more cleaner approach to retrieve requests based on entity id.

As of writing, it is preferable to preserve the service components as rxjs operators and use signals for synchronous reactivity. Effects are currently only utilized for rendering changes in the view not the business logic (you can do it using untracked()).

You may learn more about this here.

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

entityId = input.required<string>({
  alias: 'id',
});

response = toSignal(
  toObservable<string>(entityId).pipe(switchMap((id) => this.apiService.fetchById(id))),
);

Upvotes: 6

Related Questions