Mapping nested JSON to model in Angular2 - handling multiple API requests

Having trouble integrating two HTTP API Responses into my Model in Angular 2. The first API Call returns data like so:

[{
    "id": 410,
    "name": "Test customdata test",
    "layer": [79,94]
}, {
    "id": 411,
    "name": "Test customdata test2",
    "layer": 79
}]

The second request may return something like this

[{
    "id": 94,
    "name": "xy"
}, {
    "id": 79,
    "name": "abc"
}]

This is how my model is structured:

export class Dataset {
 public id: number;
 public name: string;
 public layer: Layer[];
}
export class Layer {
 public id: number;
 public name: string;
}

How can I map these JSONs to the same model? Would nested HTTP requests be necessary to retrieve all the data? If I try it like this:

this.Http.get('urltogetdataset').map(response => response.json() as Dataset[] )

I receive my Dataset Objects but the layer parameter is an array of numbers. Any suggestions on how to handle this? Thank you!

Answer №1

It appears that your API is designed to return all items per resources. If this is not the case, please let me know.

Based on this assumption, I have a proposed solution for you:

class RESTDatasetLoader {
  ...

  retrieveDataset(): Observable<Dataset[]> {    
    return Observable.of(this.jsonDataset) //<- replace this with the call to datasets
      .combineLatest(Observable.of(this.jsonLayer), //<- replace this with the call to layers
          (dataset, layers) => this.mapToDataset(dataset, layers)
      )
  }

  /*
   * Considering your question, we are making a call to the dataset resource so:
   * retrieveDataset(): Observable<Dataset[]> {    
   *     return this.Http.get('urltogetdataset')
   *        .combineLatest(Observable.of(this.jsonLayer), //<- We don't see this call mentioned in your question
   *           (dataset, layers) => this.mapToDataset(dataset, layers)
   *        )
   * }
   */


  private mapToDataset(dataset: DatasetDTO[], layers: LayerDTO[]): Dataset[] {
    return dataset.map(data => {
      const mappedLayers: Layer[] = this.mapLayers(data, layers)
      return new Dataset(data.id, data.name, mappedLayers)
    })
  }

  private mapLayers(dataset: DatasetDTO, layers: LayerDTO[]): Layer[] {
    return dataset.layer
      .map(layerId => {
        const layer = layers.find(layer => layer.id === layerId)
        return new Layer(layer.id, layer.name)
      })
  }
}

In your code, make sure to reference RESTDatasetLoader in Providers of the module and inject it into your Angular component.

You can find the complete code here: https://stackblitz.com/edit/angular-eoqebo?file=app%2Fapp.component.ts

I agree with @Aluan Haddad's suggestion to cast the response using an interface as a DTO (Data Transfer Object).

After casting the response, ensure to map it to your own object. This way, if the API response changes, you only need to update one place in your code.

There might be an error in your question regarding having one layer in the Dataset response as a number instead of an array. If intentional, consider maintaining consistent structures with the API team.

Answer №2

It is advisable to create two separate routes for this purpose, right?

/api/datasets => retrieves datasets
/api/layers => fetches layers

You can then map the responses to classes in the following way:

http.get('/api/layers').map(response => {
    return response.json().map(item => new Layer().deserialize(item));
});

I suggest incorporating a deserialization method in the classes like so:

export class Layer {
    public id: number;
    public name: string;

    public deserialize(input){
        this.id = input.id;
        this.name = input.name;
    }
}

Edit:

For handling datasets, you can use the following approach:

http.get('/api/datasets').map(response => {
    let dataset = response.json().map(item => {
        let dataset = new Dataset().deserialize(item)
        for(let layerId of item.layer){
            dataset.addLayer(layerService.getLayerById(layerId);
        }
        return dataset;
    });
});

The Dataset class should have an addLayer method for this purpose.

export class Dataset {
    public id: number;
    public name: string;
    public layer: Layer[];

    public deserialize(input){
        this.id = input.id;
        this.name = input.name;
    }

    public addLayer(layer){
        this.layer.push(layer);
    }
}

In addition, you will require a layerService to retrieve previously loaded layers based on their IDs. Here is an example implementation of the getLayerById method:

public getLayerById(id){
    for(let layer of this.layers){
        if(layer.id === id) return layer;
    }
}

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

Using setBounds in ng2-ui/map: A step-by-step guide

I have been attempting to display a map using the setBounds feature with the ng2-ui/map library. Unfortunately, I am having trouble finding documentation on how to achieve this. https://github.com/ng2-ui/map Currently, here is the code that I have writte ...

Webpack development server is failing to render the application

Currently, I am utilizing Angular Universal + CLI and my package.json configuration appears as follows: "scripts": { "lint": "tslint \"src/**/*.ts\"", "test": "ng test", "pree2e": "webdriver-manager update", "e2e": "protractor" ...

A guide to positioning the content of an Angular tag within a span element

Can someone help me figure out how to properly align the PO number and Vendor information on my page? from PO Number: 344 Vendor: yu PO Number: 3445 Vendor: yu PO Number: 344 Vendor: yu to PO Number: 344 Vendor: yu PO Number: 3445 Vendor: yu PO Num ...

Learn how to transform an object into an array consisting of multiple objects in TypeScript

The car's details are stored as: var car = {model: 'Rav4', Brand: 'Tayota'} I need to convert this information to an array format like [{model: 'Rav4', Brand: 'Tayota'}] ...

Using a pipe filter to implement a search feature in an Ionic search bar

Hey everyone, I'm facing a little issue here. I created a pipe filter to sort through some data, but now I need to include two more filters and I'm not sure how to go about it within this pipe. Below is an example of the pipe I have created: ...

Encountering errors when subscribing to a BehaviorSubject in Angular 8

I am currently working on a Single Page Application using Angular 8 where I am trying to pass data from a component to a service. In the service, I am subscribing to it using rxjs BehaviorSubject. However, when I compile the code using Angular CLI, I encou ...

The Angular Karma tests are failing because the custom tag is not recognized as a known element

I've recently started learning Angular and encountered an issue with my first Karma test. The error message I received is as follows: AppComponent should create component Error: Template parse errors: 'ereturn-form' is not a known element: ...

Utilize Node.js to extract data from a JSON object

I have a small node application that pulls stats from an httpprovider. Currently, it returns the values every second in this format: { WOWZA_CONNECTIONS_CURRENT: 21, WOWZA_CONNECTIONS_TOTAL: 4879, WOWZA_CONNECTIONS_BYTES_IN: 303242, WOWZA_CONNECTIO ...

Encountered a React TypeScript issue stating that the type '{ ... }' cannot be assigned to the type 'IntrinsicAttributes & IntrinsicClassAttributes<...>'

Embarking on a new journey with a react typescript project, I encountered this puzzling error: Failed to compile. /Users/simon/Code/web/react-news-col/src/MainNewsFeed.tsx TypeScript error in /Users/simon/Code/web/react-news-col/src/MainNewsFeed.tsx(27,35 ...

Creating a React Native project without the use of TypeScript

Recently I dived into the world of React Native and decided to start a project using React Native CLI. However, I was surprised to find out that it uses TypeScript by default. Is there a way for me to create a project using React Native CLI without TypeS ...

How can I set up server-side rendering in Laravel using Angular?

For my single page application built with Angular 5, I decided to integrate it with a Laravel backend. In the Laravel project, I stored all my Angular files within an 'angular' folder. However, I built the actual Angular application outside of th ...

"Exploring the process of connecting a `mat-select` element with dynamically changing data

Having trouble binding data from an API to a mat-select control due to the asynchronous nature of the call. The issue arises because the mat-select control is set before the results are fetched. Any suggestions on how to resolve this? app.component.html ...

What is causing the md-menu options to not be injected into my hybrid Angular application?

I am currently troubleshooting an issue in my hybrid Angular/AngularJS application that arises upon reloading. To see a demonstration of this issue, visit this StackBlitz link. The approach I am using to bootstrap AngularJS within an Angular app is largely ...

Troubleshooting Problem: Incompatibility with Angular 1 and Angular 2 Hybrid Application causing Display Issue with Components

In my development work, I have created a unique hybrid application that combines Angular 1 and Angular 2 functionalities. This hybrid setup was achieved by following the guidelines provided in two helpful resources: Upgrading from AngularJS and Migrating A ...

What is the best way to combine the contents of one file into another file while maintaining the appropriate formatting using a shell

I am dealing with two JSON files - template.json and historique.json. The template.json file contains the following data: { "PTF_INSTALL_DATE": " 2020-03-31 09:12:10", "PTF_CONTENT": [ { "NAME": "api_batch_API", "CHECKED": "a ...

Angular HTTP client fails to communicate with Spring controller

Encountered a peculiar issue in my Angular application where the HttpClient fails to communicate effectively with the Spring Controller. Despite configuring proper endpoints and methods in the Spring Controller, the Angular service using HttpClient doesn&a ...

Ways to export redux store data to an external JSON file?

My Redux store is structured as object key value pairs listed below: { "appElements": { "layers": { "layer_1": { "scene": { "width": "100px", "height": "100px", "bgColor": "#aaaaaa", "bgImage": " ...

Create a streaming service that allows for multicasting without prematurely ending the main subject

In my implementation of caching, I am utilizing BehaviorSubject and multicast. The cache stream should begin with an HTTP request and I want the ability to manually trigger a cache refresh by calling next on the subject. While the conventional method of us ...

Tips on creating transit-clj within a document and extracting the data format from said document

If I have the following data: (def c [{:id 12 :name "John"}]) What is the process for saving this data in a file? How can I retrieve this data structure later on? ...

Angular 11 error: Datatable type is not defined

We have a services.ts file that requires 2 arguments. UPDATE: It appears that I was looking at the error incorrectly. This method is causing the console error: private checkLink(row, options) { if (options.type === 'link' || options.type ...