4

I am trying to use forkJoin to run an array of objects with a key and a value which is the observable. I need to make it to where the array of objects will result in the key being the same and the value being the result of the observable api call. This object can have any number of key value pairs. And I need to wait till they are all done before I can continue.

If I have:

{
  headerLogo: this.http.post(url, image),
  loginBackground: this.http.post(url2, image)
}

I need it to result in:

{
  headerLogo: resultOfApiCall
  loginBackgroud: resultOfApiCall2
}

I have tried using ForkJoin but it seems to only take an array of just the observables and no keys. They keys are important so that I know what URL goes with what key so I can save them in the correct spot. The original fork join will return an array of objects which represent the result of the api calls but I have no idea which one belongs to which key.

Any thoughts on this?

4 Answers 4

12

From RxJS v6.5+, you could use a dictionary (key value pairs) as sources.

Example and Stackblitz sourced from Learn RxJS:

const { forkJoin } = rxjs;
const { ajax } = rxjs.ajax;

const jsonEditor = document.getElementById('json-editor');
const textContainer = document.getElementById('text-container');

const options = {};
const editor = new JSONEditor(jsonEditor, options);

/*
  when all observables complete, provide the last
  emitted value from each as dictionary
*/
forkJoin(
  // as of RxJS 6.5+ we can use a dictionary of sources
  {
    google: ajax.getJSON('https://api.github.com/users/google'),
    microsoft: ajax.getJSON('https://api.github.com/users/microsoft'),
    users: ajax.getJSON('https://api.github.com/users')
  }
)
.subscribe({
  next: (response) => editor.set(response),
  error: (error) => textContainer.innerHTML = error
});
<link href="https://unpkg.com/[email protected]/dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/jsoneditor-minimalist.min.js"></script>

<div id="json-editor" style="width: 600px; height: 400px;"></div>
<span id="text-container"></span>

Sign up to request clarification or add additional context in comments.

6 Comments

Almost there the only issue is when I console log it logs once for every key value pair. How will I know when all are completed? With a normal forkJoin it only logged once when all were done.
forkJoin will only emit when all the observables are complete.
This is really nice.
Is there a way to store ajax.getJSON('https://api.github.com/users') in more than one key without reaching the backend twice?
@LeonardoRick: For a one-time call you could just tack in a map operator to the HTTP request and return an object with different keys pointing to the same response. See here. If you wish to "cache" the HTTP response, you could use the shareReplay operator with buffer 1. See here
|
4

Since your example doesn't show an array of objects, but just an object I assume that is what you are using. You can access the properties of the resulting object ( note that I am using object destructoring in the subscribe, because it looks nice and clean ):

forkJoin({
  headerLogo: this.http.post(url, image),
  loginBackground: this.http.post(url2, image)
})
.subscribe( ({headerLogo, loginBackground}) => {
  // .....
})

3 Comments

using the object/dictionary forkJoin does not seem to compile in Typescript/Angular...
@cevaris Did you import forkJoin? Also I made a small typo loginBackgroud, instead of loginBackground that may also be the cause (fyi: corrected that).
idk, i was using my custom observables, not the example observables provided in this forkJoin example.
0

forkJoin will return results in the same order. It means:

forkJoin([
  this.http.post(url, image),
  this.http.post(url2, image),
]).subscribe(([resultOfApiCall, resultOfApiCall2]) => {
  // save result of API1 with resultOfApiCall
  // save result of API2 with resultOfApiCall2
})

1 Comment

I still don't see how I can associate the result with the correct key here. And I can have any number of images so manually saving is not efficient since I don't know how many there will be
0

Javascript Objects are not ordered so if you get just an array of observables back, you can't be sure that it belongs to one particular key.

Lets try this way, get key, value pairs first, as an Array. We will use the keys even as Observables and apply forkJoin over both keys and actual requests so that, when they come, they come together. Finally, We can apply a forkJoin over all the forkJoins and convert the results back to an Object using a map.

    const myObject = {
        headerLogo: this.http.post(url, image),
        loginBackground: this.http.post(url2, image)
    }

    const keyValuesAsObservables = Object.entries(myObject).map(([k, v]) => {
        return forkJoin(of(k), v);
    });

    forkJoin(...keyValuesAsObservables).pipe(map(res) => {
        return Object.fromEntries(res);
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.