Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

190
Visualizações
Best way to get intermediate data from an RXJS observable

I want to call the first service to query for some data (returns an array of results), and then make another call to a second service to get more data for each of those results. The results from each service call should be combined and returned and displayed in a single list. I think I understand how that part works.

The part I was less sure of was the requirement to be able to display the results from the first service call immediately when available. The UI should display loading indicators for the fields which come from the second service call before that data is available.

The only thing I can think of is to add "tap" rxjs operators after the first service call and the second service call which emit events on a new subject and the UI could listen to the _documents$ Subject to show the data after the first call, and show the combined data after the second call.

Are there any best practices for handling this scenario? Am I missing another pattern or rxjs operator that would make this simpler?

  _documents$: BehaviorSubject<DocumentSearchSomething[]> = new BehaviorSubject([]);
  private documentSearchQuery$ = new Subject<string>();

  constructor(private store: Store, private http: HttpClient) {

    this.documentSearchQuery$.pipe(
      switchMap((query) => {
        return this.http.get<any[]>(`https://my_api.com​/service_1?q=${encodeURIComponent(query)}`)
          .pipe(
            tap(service_1_results => this._documents$.next(service_1_results)),
            switchMap(service_1_results => {
              // make call to service 2 to get more data for each result, pass in id of every result
              return this.http.post<any[]>(`https://my_api.com/service_2`, {
                ids: service_1_results.map(r => r.id)
              }).pipe(
                map(service_2_results => {
                  // combine service_1_results with service_2_results
                  const combined = service_1_results.map(r1 => {
                    const r2 = service_2_results.find(c => c.id === r1.id);
                    return {...r1, ...r2};
                  });
                  return combined;
                }),
                tap(combined_results => this._documents$.next(combined_results))
              );
            })
          );
      }),
    ).subscribe();
  }
about 4 years ago · Juan Pablo Isaza
3 Respostas
Responde à pergunta

0

Yes, there is an operator you can use to simplify this. Since this pipe sets up an inner observable (that results in emitting the combined values), you can use startWith() to immediately emit the value of service one from the inner observable.

    _documents$: Observable<DocumentSearchSomething[]>;
private documentSearchQuery = new Subject<string>();

constructor(private store: Store, private http: HttpClient) {

  this._documents$ = this.documentSearchQuery.pipe(
    switchMap(query => this.http.get<any[]>(`https://my_api.com​/service_1?q=${encodeURIComponent(query)}`)),
    switchMap(serviceOne => this.HTMLOutputElement.post<any[]>(`https://my_api.com/service_2`, {
      ids: serviceOne.map(r => r.id)
    }).pipe(
      map(serviceTwo => serviceOne.map(r1 => {
        const r2 = serviceTwo.find(c => c.id === r1.id);
        return { ...r1, ...r2 };
      })),
      startWith(serviceOne)
    ))
  );

}
about 4 years ago · Juan Pablo Isaza Relatório

0

Something like this?

  private documentSearchQuery$ = new Subject<string>();

  private documents1$: Observable<DocumentSearchSomething[]> =
    this.documentSearchQuery$.pipe(
      switchMap((query) =>
        this.http.get<DocumentSearchSomething[]>(
          `https://my_api.com​/service_1?q=${encodeURIComponent(query)}`
        )
      ),
      shareReplay({ bufferSize: 1, refCount: true })
    );

  private documents2$: Observable<DocumentSearchSomething[]> =
    this.documents1$.pipe(
      switchMap((res) =>
        this.http.post<any[]>(`https://my_api.com/service_2`, {
          ids: res.map((r) => r.id),
        })
      ),
      shareReplay({ bufferSize: 1, refCount: true })
    );

  private combinedResults$ = combineLatest([documents1$, documents2$]).pipe(
    map(([res1, res2]) =>
      res1.map((r1) => {
        const r2 = res2.find((c) => c.id === r1.id);
        return { ...r1, ...r2 };
      })
    )
  );
about 4 years ago · Juan Pablo Isaza Relatório

0

If you are handling an array response with an arbitrary length, and need details for each item in the response, then without going into a more advanced configuration of index tracking, Subjects, and merged Observables, you might could try something like this:

https://stackblitz.com/edit/angular-ivy-ae7sdt?file=src/app/app.component.ts

In this example, a details key is added to each element in the expected response array. This property is mapped to an Observable with an initial value that indicates loading is going on.

    this.todos$ = this.getTodos().pipe(
      map((todos: Todo[]) =>
        todos.map(todo => ({
          ...todo,
          details: this.getTodo(todo.id).pipe(
            startWith({ completed: 'loading' }),
            map(resp => String(resp.completed))
          )
        }))
      )
    );

Then, in the template, you would use the async pipe to bind to the result. I don't believe this to be best practice, but for a short, easy solution it should work.

A more robust answer would be to use a BehaviorSubject with an array, and an observable that exposed it's value. This array would map to the additional details for each item in the parent array. You could then iterate over both in the template like so:

<ng-container *ngIf="{
    todos: todos$ | async,
    todoDetails: todoDetails$ | async
} as state ">

  <div *ngFor="let todo of state.todos; let i =index">
    {{ todo.title }}
    {{ state.todoDetails[i] }}
  </div>
</ng-container>
about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda