NiceToMytyuk
NiceToMytyuk

Reputation: 4277

How to run Observable simultaneously but combine the response in Angular?

In my Angular App i'm making the call to two APIs, one returns generic data about X items and another API returns it's images.

I would do something like run the two calls simultaneously, show the generic data as that API is the one which response should be faster and then i would change the objects i get from the info api with the images get from the other API.

For now my code is a forkJoin of two APIs but at that point i will show the items only when both APIs are completed.

Here is my code:

  ngOnInit(): void {

    forkJoin([this.negoziService.negozi(), this.negoziService.images()]).subscribe(data => {
      this.arrNegozi = data[0];
      data[1].map((i) => {
        const image = this.arrNegozi.find((d) => d.id === i.id);
        image !== undefined
          ? image.logo = 'data:image/jpg;base64,' + i.img
          : image.logo = null;
      });
    }, error => {
      this.errore = error;
    })
  }

EDIT:

i've just changed my code to combineLatest as suggested in comments but the code still behave the same way.

combineLatest([this.negoziService.negozi(), this.negoziService.images()]).subscribe(([nagozi, images]) => {
  this.arrNegozi = nagozi;
  images.map((img) => {
    const negozio = this.arrNegozi.find((d) => d.id === img.id);
    negozio !== undefined ? negozio.logo = 'data:image/jpg;base64,' + img.img : negozio.logo = 'assets/images/no_image.svg';
  })
  },
  error => {
    this.errore = error;
  }
)

The usage is to show a skeleton while the images are loading but as soon as it's possible show the text data got from the first API.

Upvotes: 1

Views: 108

Answers (1)

I think you you can make the independent requests, and take control about responses and how to merge them. In this way you take control over arrNegozi and show them, at the same time take control over arrImages, and show skeleton until arrNegozi are loaded. First try to manage both info as attributes,

arrNegozi: any[];   // I dont know type of your attribute
arrImages: any[] = [];

in ngOnInit You make separates request and manage when merge negozi with images

 this.negoziService.negozi().subscribe((negozi) => {
  this.arrNegozi = negozi;

// if the request for images response first, and arrImages are already loaded and has data, then merge negozi with images
   if(this.arrImages.length > 0){
     this.mergeNegoziWithImages();
  }
},
  error => {
    this.errore = error;
  }
)

 this.negoziService.images().subscribe((images) => {
// save data on your attribute 
  this.arrImages = images;

// If the request for negozi response first and  arrNegozi are already loaded and has data, then merge negozi with images

// 
  if(this.arrNegozi.length > 0){
     this.mergeNegoziWithImages();
  }
  },
  error => {
    this.errore = error;
  }
)

Implement method to merge your arrNegozi and your arrImages when get available.

mergeNegoziWithImages(){
// I wrote this guessing about your algorithm, feel free to make your own implementation of the union
this.arrImages.foreach((i)=> {
const image = this.arrNegozi.find((d) => d.id === i.id);
        image !== undefined
          ? image.logo = 'data:image/jpg;base64,' + i.img
          : image.logo = null;
})

// Maybe you have to slice arrNegozi to change reference in memory to that array and generate ngOnChanges to be execute.
this.arrNegozi = this.arrNegozi.slice();
}

Upvotes: 1

Related Questions