What is the best way to execute multiple Observables concurrently in Angular and merge the results?

Within my Angular application, I am fetching data from two different APIs. The first API provides generic information about a set of items, while the second API returns corresponding images for those items.

To optimize the loading process, I have implemented a method where both API calls are made simultaneously. Once the generic data is retrieved (which should be faster), I display it to the user. Subsequently, I replace the generic data objects with the images obtained from the second API.

Currently, I am using forkJoin to combine the results of both API calls. However, this means that the items will only be displayed once both APIs have completed their tasks.

Below is a snippet of 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 recently updated my code to use combineLatest as recommended in some comments, but the behavior remains unchanged.

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 intent behind this approach is to initially show a skeleton layout while the images are still loading. Once the text data from the first API is available, it should be displayed promptly.

Answer №1

If you want to make separate requests and have control over how the responses are merged, here's a way to do it: You can handle arrNegozi and arrImages independently by controlling when they are loaded and displayed. Start by treating both sets of information as attributes:

arrNegozi: any[];   // The type of this attribute is unknown
arrImages: any[] = [];

In the ngOnInit method, you can make separate requests and manage the merging of negozi with images:

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

  // If images are already loaded before negozi, merge them
   if(this.arrImages.length > 0){
     this.mergeNegoziWithImages();
  }
},
error => {
this.error = error;
}
)

this.negoziService.images().subscribe((images) => {
// Store the image data in your attribute 
this.arrImages = images;

// If negozi are already loaded before images, merge them

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

Implement a method to merge arrNegozi with arrImages once both sets of data are available.

mergeNegoziWithImages(){
// Here is a generic algorithm for merging the two arrays, but feel free to customize it as needed
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;
})

// You may need to slice the arrNegozi array to trigger ngOnChanges and update the reference in memory
this.arrNegozi = this.arrNegozi.slice();
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Data bindings encapsulated within nested curly braces

I am currently utilizing Angular2 in my application. Within my html file, I have a *ngFor loop set up like so: <div *ngFor="let element of array"> {{element.id}} </div> Next, I have an array containing objects structured as follows: some ...

Is it possible to only select items within the current PageSize in Mat-Table?

I am currently developing a table with pagination that involves performing certain actions based on an array of selected checkboxes. I have referred to the documentation and everything seems to be working fine when it comes to selecting individual rows or ...

Guide on refreshing a page from a different class using Ionic3

I have two main folders named /HomePage and /SettingsPage. /HomePage consists of: home.html home.ts The second folder, /SettingsPage, includes: settings.html settings.ts My goal is to refresh the content in HomePage (home.html) using code from set ...

Error: A stream was expected, but you provided 'undefined'. Please provide either an Observable, Promise, Array, or Iterable instead

I'm encountering an error while trying to catch errors in my Ionic-based application with Angular. In the create() method, I am attempting to create a new User. If the username already exists, I receive a response from the backend, but my method thro ...

Sending data from view to controller in Angular using TypeScript

I am currently working with AngularJS and TypeScript, and I have encountered an issue with passing a parameter from the view to the controller. In my attempts to solve this problem, I have utilized ng-init as follows: <div class="col-md-9" ng-controlle ...

Add a value in front of switchMap along with a Promise

Whenever a new value is emitted by this.selectedLanguage$, I need to emit a value that is calculated asynchronously. My current approach to this requirement involves the following code: public readonly languageCategories$ = this.selectedLanguage$.pipe( ...

I'm having trouble locating a declaration file for the module 'vue-prism-component'

Currently utilizing Vue 3 (Composition API), Vite, and Typescript but encountering a missing module issue with vue-prism-component. <script lang="ts" setup> import 'prismjs' import 'prismjs/themes/prism-tomorrow.css' imp ...

Enhancing the design of the Mat Calendar with a stylish border

I've been attempting to put a border around my mat calendar, but I'm having trouble figuring out the specific class that will give me the exact look I want. I need it to be just like this: https://i.sstatic.net/fTGbp.png I tried using the follow ...

Styling for 'checked' state within an ngFor iteration

Each checkbox in my list is assigned to a different department, with each department having its own color. When a box is unchecked, only the border is in the color of the department. When checked, the background changes to the assigned color https://i.ssta ...

Utilizing CORS and Basic Authentication in Embedded Jetty with ConstraintSecurityHandler

I have an embedded Jetty server running on localhost:8008 and another Angular application accessing it on localhost:4200. I was able to implement CORS by adding the CrossOriginFilter to the svrContext where my webservices are located. However, when I set ...

Revolutionize your Angular applications with dynamic template loading techniques

I recently developed a new component called componentB, which shares a similar template with another component, componentA. Currently, when a specific url is accessed, the original componentA is loaded. However, I want to load a slightly modified template ...

Showing Arrays in Angular on HTML Page

I have created an array that stores multiple arrays with 3 indexes each. An example of the structure looks like this: (3) [Array(3), Array(3), Array(3)] 0: (3) [199.4, 10.5, 19] 1: (3) [47.2, 2.1, 23] 2: (3) [133.6, 5.3, 25] In my HTML, I want to display ...

How do I fix the build error that says "Operator '+' cannot be used with types 'number[]'?

The function below is designed to generate unique uuidv4 strings. function uuidv4() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => ( c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)) ...

Setting up Emotion js in a React TypeScript project using Vite 4

Currently, I am in the process of transitioning from Webpack to Vite for my React Typescript application. I have been attempting to integrate Emotion js into the project. "@vitejs/plugin-react": "^4.0.1", "vite": "^4.3.9 ...

transformed an Angular 2 web application into a sleek and functional mobile application

I am looking to convert my basic angular2 web application into a mobile app using cordova. Is there a way to achieve this without relying on Ionic or nativeScript? ...

Tips for adding a "Select All" feature to a dropdown list?

Currently, I have a dropdown list with a filter for IN and OUT values. The functionality is working as expected: <select class="form-select" style="max-width: 100px" [ngModel]="selectedBrand" (ngModelChange)="onChangeT ...

Despite having installed v18.3, the Angular CLI specifically demands a minimum Node.js version of either v14.20, v16.14, or v18.10

Today I decided to upgrade my Angular CLI from v14.1 to v16. After upgrading, I encountered an issue every time I tried to run ng, which indicated that the CLI required a minimum version of node.js: The Angular CLI requires a minimum Node.js version of ei ...

Extend the express request object with Typescript and then export the modified object

Seeking to enhance the Request object from express with custom fields using typescript. Based on this particular source, I created a file named @types/express/index.d.ts containing the following code : import { MyClass } from "../../src/MyClass" ...

Differences between Angular TS Lint onInit and ngOnInit

My TS Lint issue warned me to implement the OnInit interface and included a link to this page: https://angular.io/docs/ts/latest/guide/style-guide.html#!#09-01 I'm curious, what sets apart `onInit` from `ngOnInit`? Both seem to work just fine for me. ...

Angular module with customizable configurations

I am interested in developing a customizable Angular 9 module with IVY and AOT enabled. In the latest version of Angular, IVY and AOT are automatically activated: npx @angular/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ed8 ...