Retrieve the ultimate information from the Angular service

Within my angular 6 project, I am dealing with a product_id array,

product_id: any = ["123", "456"];

ngOnInit :

  ngOnInit() {
    this.product_id.forEach(element => {
      this.httpClient.get('https://api.myjson.com/bins/hiolc').subscribe(res => {
        this.productList = [];
        res.products.forEach( item => {
          this.productList.push(item);
        });
      })
    })
    console.log(this.productList);
  }

When I try console.log(this.productList);, my expected outcome should be:

   [{
      "product_name": "Product One",
      "product_information": {
        "template_details": [
          {
            "template_name": "RACE Template",
            "productProperties": [
              {
                "property_id": 2518427931,
                "property_name": "Length",
                "property_value": "12 cm"
              },
              {
                "property_id": 2621195440,
                "property_name": "Width",
                "property_value": "10 cm"
              },
              {
                "property_id": 2621195441,
                "property_name": "Height",
                "property_value": "20 cm"
              }
            ]
          }
        ]
      }
    },
    {
      "product_name": "Product Two",
      "product_information": {
        "template_details": [
          {
            "template_name": "RACE Template",
            "productProperties": [
              {
                "property_id": 2518427931,
                "property_name": "Length",
                "property_value": "15 cm"
              },
              {
                "property_id": 2621195440,
                "property_name": "Width",
                "property_value": "12 cm"
              },
              {
                "property_id": 2621195441,
                "property_name": "Size",
                "property_value": "Medium"
              }
            ]
          }
        ]
      }
    }]

Instead, I only get an empty array []..

How can I ensure that the data is stored in productList only after the service has completed?

I need to use forEach with this.product_id.forEach(element to retrieve the product list for each product id sent through the URL in my actual application..

Please assist me in storing the data in productList only after all product ids have been processed and the final product list is obtained..

Working stackblitz: https://stackblitz.com/edit/flatternstructure-dvzpjv

Once again, I emphasize that in my real application, I need to pass the id to retrieve the product value like

https://api.myjson.com/bins/hiolc + element
..

Furthermore, once I have the final productList, I need to perform additional functionality with it, which is why I expect the complete data in productList at the end and not within the forEach loop..

Answer №1

Consider using rxjs.concat method for concatenating observables:

[edit]

For a working example, check out the code on stackblitz

...
import { concat } from 'rxjs';
import { map } from 'rxjs/operators';
...

ngOnInit() {
  this.productList = [];
  concat(
    this.product_id.map(
      element => this.httpClient.get('https://api.myjson.com/bins/hiolc')
    )
  ).pipe(
    map((res: any) => this.productList = this.productList.concat(res.products))
  ).subscribe(res => {
    console.log(res);
    console.log(this.productList);
  });
}

Hope this solution works for you.

Answer №2

There are a couple of issues with your current approach. First, you are logging the data too soon before the requests are even completed. This results in an empty array being displayed. Move the console.log statement below the line where the data is pushed into the array to see the correct results.

The second issue is that you are overwriting the product list with each parallel request. You should only reset the product list once.

A more efficient approach would be to use the forkJoin operator to fire off parallel requests and concatenate the results into the product list. This will provide a cleaner and simpler implementation.

ngOnInit() {
  forkJoin(product_ids)
    .pipe(
       map(id => this.http.get('https://api.myjson.com/bins/hiolc' + id)),
       map(items => this.productList = this.productList.concat(items)),
    )
    .subscribe(() => console.log('Done, items:', this.productList));

Alternatively, you can move your HTTP calls into a service and subscribe to the getProductItems method to update the product list.

 ngOnInit() {
    this.someService.getProductItems(productIds)
        .subscribe(productList => this.productList = productList);
}

By organizing your code in this manner, you can keep your component clean and focused on its core functionality.

Answer №3

Have you considered developing a genuine service in line with the angular documentation?

It seems like you are attempting to integrate the httpClient directly into a component. You can refer to this link for more information: https://angular.io/tutorial/toh-pt4#subscribe-in-heroescomponent

My suggestion would be to start by creating an Injectable service that can be utilized in the component constructor, similar to how you utilize the httpClient.

Next, retrieve the data in the service and subscribe to it for better functionality.

Answer №4

Why not give this a shot:

ngOnInit() {
    let counter = 0;
    this.itemsList = [];
    this.item_id.forEach(element => {
      this.httpClient.get('https://api.myjson.com/bins/hiolc').subscribe(res => {
        res.items.forEach( item => {
          this.itemsList.push(item);
        });
        counter = counter + 1;
        if(counter === this.item_id.length){
            console.log(this.itemsList);  
            // Execute your next function here
        }
      })
    })
}

Answer №5

Latest Update:

Here is the solution that can be implemented:

import { combineLatest } from 'rxjs'
....
ngOnInit() {
  const observables = []
  this.product_id.forEach(id => {
    observables.push(this.httpClient.get('https://api.myjson.com/bins/hiolc'))
  })
  combineLatest(...observables).subscribe(response => {
    response.forEach(r => this.productList.push(...r.products))
    console.log(this.productList)
  })
}

Visit stackblitz for live demo: https://stackblitz.com/edit/flatternstructure-nk9yha

Answer №6

How about checking out this code snippet? StackBlitz

import { forkJoin } from 'rxjs';

....

ngOnInit() {
    forkJoin(
      this.product_id.map(element => this.httpClient.get('https://api.myjson.com/bins/hiolc'))
    )
    .subscribe((res) => {
        this.productList = this.productList.concat(res);
        console.log(this.productList);
    });
}

Answer №7

Another way to achieve the same result is:

this.items$ = from(this.item_id).pipe(
    switchMap(() =>  this.httpClient.get('https://api.yourjson.com/bins/abc123')),
    scan((items, item) => [...items, item], [])
    tap((items) => console.log(items))
).subscribe(() => {}) // or use async pipe in a HTML template

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

How can I decrypt a JWT token using Angular?

One of my current tasks involves decoding a jwt token that is provided by the API during the login process. Can anyone provide guidance on how to decode a jwt token in an Angular application? ...

Utilize a directive every instance

I need to implement an angular directive that triggers before all events like ng-click whenever the view value changes. This directive should be called as the first action when the view is changed. Check out the JSFiddle example. angular.module('myA ...

Obtain a union type in TypeScript based on the values of a field within another union type

Is it feasible in Typescript to derive a union type from the values of a field within another union type? type MyUnionType = | { foo: 'a', bar: 1 } | { foo: 'b', bar: 2 } | { foo: 'c', bar: 3 } // Is there an automati ...

Should I link my Angular Material 2 data table to AngularFire2 or Firebase service?

Trying to make this work has been quite the struggle. I've spent hours experimenting, but nothing seems to be working as expected. The md data table is relatively new, so there isn't much information available online yet. Connecting Firebase to t ...

TypeScript has two variable types

I'm facing a challenge with a function parameter that can accept either a string or an array of strings. The issue arises when trying to pass this parameter to a toaster service, which only accepts the string type. As a result, when using join(' ...

What is the best method for determining the cookie expiration time in AngularJS 1.3?

Currently in my application, I am utilizing AngularJS 1.3. I encountered a challenge while using $cookies to store data where I needed to implement a 1-minute expiration time for the cookie. However, the $cookies service in AngularJS 1.3 does not provide ...

The request to sign up at 'https://identitytoolkit.googleapis.com/v1/accounts:/signUp? from the origin 'http://localhost:8080' has been denied

My attempt to create a new user in Firebase using Axios in Vue.js is resulting in an error message regarding CORS policy. The specific error states: "Access to XMLHttpRequest at 'https://identitytoolkit.googleapis.com/v1/accounts:/signUp?key=AIzaSyDvZ ...

The implementation of CORS headers does not appear to function properly across Chrome, Firefox, and mobile browsers

I encountered an issue while trying to consume a third party's response. The functionality works correctly in Internet Explorer, but fails in Chrome, Firefox, and on my mobile browser. Despite searching online and testing various codes, I continue to ...

What causes an undefined outcome when a promise is fulfilled?

Could you help me understand something? const promise1 = new Promise((resolve, reject) => { setTimeout(() => { if(true) { resolve('success') } else { reject('failure') } }, 4000) }) promise1.then(resul ...

JavaScript/CSS manipulation: The power of overriding animation-fill-mode

When animating text using CSS keyframes, I use animation-fill-mode: forwards to maintain the final look of the animation. For example: #my-text { opacity: 0; } .show-me { animation-name: show-me; animation-duration: 2s; animation-fill-mod ...

Guide to including objects into your project without the need for babel through the use of CDN

I'm struggling with getting my Vue code to transpile properly due to some issues. I have resorted to loading Vue and other packages directly using CDN links, like this: <script src="https://cdnjs.cloudflare.com/ajax/libs/survey-vue/1.8.33/surv ...

Storing array data locally within an AngularJS application for seamless sharing between two separate applications

I need assistance with persisting and sharing array data var queries = []; between two separate AngularJS applications. One application is for the front end, while the other is for the admin side. Both applications cause the entire page to load when access ...

Permitted the usage of a global variable of any type as the return value of a function that does not explicitly define its

Here's a snippet of TypeScript code that compiles successfully: let testVar: any; const testFunc: () => number = () => { return testVar; }; Why does this code compile without errors? What is the reasoning behind it? ...

Having issues with default sorting and searching not functioning in Datatables with Angularjs

Utilizing a directive to facilitate database building once ng-repeat has completed: app.directive('repeatDone', function() { return function(scope, element, attrs) { if (scope.$last) { scope.$eval(attrs.repeatDone); ...

Having issues with custom directives not functioning properly within AngularJS' RouteProvider

Currently, I'm in the process of learning AngularJS and specifically focusing on the route provider feature. I have successfully built a few pages that functioned well independently. Now, my aim is to implement the route provider into my project. In m ...

Input form with multiple fields. Automatically display or hide labels based on whether each field is populated or empty

I am attempting to create a form where the placeholders move to the top of the input when it is focused or filled. However, I have encountered an issue with having multiple inputs on the same page. Currently, my JavaScript code affects all inputs on the pa ...

"Filtering a JSON File Based on Button Data Attributes: A Step-by-

I am working with a set of buttons that have specific data-map attributes as shown below: <button class="btn btn-default mapper" data-map="2015-11-13">Monday</button> <button class="btn btn-default mapper" data-map="2015-11-14">Tuesday&l ...

Troubleshooting fastify library errors related to ajv validation

Every time I try to build my TypeScript code, I encounter these errors: The following errors are showing up in my TypeScript code: 1. node_modules/@fastify/ajv-compiler/types/index.d.ts(1,10): error TS2305: Module 'ajv' has no exported member ...

Tips for displaying the default keyboard with numbers and symbols on Ionic applications

Is there a way to display a keyboard like the image provided by default in an Ionic5 app for Android? I attempted using type="number", but it restricts input to numbers only. However, on iOS, the full keyboard is displayed with this type. When I ...

Pass the form data to a Bootstrap modal upon submission of the form

I'm attempting to retrieve form data and convert it to JSON to display in a modal dialog that opens on the Submit button click! This is my progress so far: HTML <form class="form-horizontal" id="configuration-form"> --irrelevant-- <button ...