Zach Starnes
Zach Starnes

Reputation: 3198

RXJS Using ForkJoin with objects

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?

Upvotes: 4

Views: 8884

Answers (4)

Rob Monhemius
Rob Monhemius

Reputation: 5144

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}) => {
  // .....
})

Upvotes: 4

Barremian
Barremian

Reputation: 31125

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>

Upvotes: 12

Ashish Ranjan
Ashish Ranjan

Reputation: 12960

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

Upvotes: 0

HTN
HTN

Reputation: 3604

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

Upvotes: 0

Related Questions