0
votes

I want to pass URL besides getting its response data in next pipable operator of RxJS. What do you think is the smartest way to achieve this? Thanks in advance.

Here is an example. https://stackblitz.com/edit/angular-rxjs-passing-data-to-next-operator-question

I tried some operators, but I couldn't find right one. (Actually, I don't even know why passing function which returns observable to mergeMap results in getting data as parameter of function in next operator...)

from([
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/comments',
  'https://jsonplaceholder.typicode.com/albums',
])
  .pipe(
    mergeMap(url => this.getData(url)),
    tap(posts => console.log(posts[0])), // I want to get url too here!!
  ).subscribe();

I expect to get url and its response data as pair in pipable operator.

3

3 Answers

8
votes

You can map the response to whatever you want:

from([
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/comments',
  'https://jsonplaceholder.typicode.com/albums',
]).pipe(
    mergeMap(url => this.getData(url).pipe(
      map(response => ({ response, url })),
    )),
    tap(response => console.log(response)),
  ).subscribe();
1
votes

Looking at the signature of mergeMap mergeMap(project: function: Observable, resultSelector: function: any, concurrent: number): Observable, you can use the resultSelector argument:

from([
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/comments',
  'https://jsonplaceholder.typicode.com/albums',
])
  .pipe(
    mergeMap(url => 
      this.getData(url),
      (outerValue, innerValue) => ({ url: outerValue, posts: innerValue })),
    tap(({ posts, url })=> {
      console.log(posts);
      console.log(url);
    })
  ).subscribe();

This will effectively map both url and the result of this.getData(url) to an object that can be used in tap().

Here is your example modified to show this in action.

NOTE: result selectors are in the process of being deprecated/removed. While this solution may currently work, it will no longer be viable in future version of RxJS (7.x). The answer provided by @martin is definitely more "future-proof".

Hopefully that helps!

0
votes

Martin's answer is the correct one for the given code sample.

However for a more complex and longer chain of operators, passing previous values along to subsequent operators can become a quite complex task. Especially while using more advanced operators such as the expand operator.

In those cases, I prefer using a closure to store those values:

const myStream$ = () => {
   const urls = [
      'https://jsonplaceholder.typicode.com/posts',
      'https://jsonplaceholder.typicode.com/comments',
      'https://jsonplaceholder.typicode.com/albums',
  ];
  let dataUrl;
  return from(urls).pipe(
      mergeMap(url => {
         dataUrl = url;
         return this.getData(url)
      }),
    // ... assuming more operators are called here ... //
    tap(posts => console.log(posts[0], dataUrl))
  );
};

myStream$().subscribe();

But again, for a more simple chain of operators, adding values to a returned object is the way to go.