Effectively managing intricate and nested JSON objects within Angular's API service

As I work on creating an API service for a carwash, I am faced with the challenge of handling a large and complex json object (referred to as the Carwash object). Each property within this object is essentially another object that consists of a mix of simple data types and additional custom objects that need to be instantiated. While examples for objects with fewer levels of properties utilizing the RxJs map function are plentiful, dealing with deeply nested objects like this presents a different set of hurdles.

Overview of Carwash.model.ts

export class Carwash {
public name: string;
public type: CARWASH_TYPE;
public rating: Rating;
public address: Address;
private _coordinates: CarwashCoordinates;
public promotions: Promotion[];
public silverPackage: Package;
public goldPackage: Package;
public platinumPackage: Package;
public exteriorPackage: Package;
public interiorPackage: Package;
public completePackage: Package;
public storeHours: StoreHours;
}

ApiService.ts

public static carwashUrl = 'http://localhost:4200/assets/data/carwash.json';
private static carwashObject: Carwash;
public name: string;
public type: CARWASH_TYPE;
public ratings: Rating[];
public address: Address;
private _coordinates: CarwashCoordinates;
public promotions: Promotion[];
public silverPackage: Package;
public goldPackage: Package;
public platinumPackage: Package;
public exteriorPackage: Package;
public interiorPackage: Package;
public completePackage: Package;
public storeHours: StoreHours;
constructor(private http: HttpClient) {
}

public getCarwash() {
    console.log('getCarwash called');
    if (!CarwashService.carwashObject) {
        this.fetchCarwash().subscribe(
            (carwash => CarwashService.carwashObject = carwash)
        );
    } else {
        console.log('Carwash already fetched.');
    }
}

private fetchCarwash(): Observable<Carwash> {
    return this.http.get<any>(CarwashService.carwashUrl).pipe(
        map(res => {
            const carwashData = res.json();
            this.name = carwashData.name; // Handling top level data is straightforward
            this.type = carwashData.type; // Handling top level data is straightforward
            for (const rating of carwashData.ratings) {
                this.ratings.push(new Rating(rating.customerName,
                    rating.score,
                    rating.review,
                    rating.date)); // Handling top level data is straightforward
            }
            this.address = // Figuring out how to instantiate custom objects elegantly poses a challenge.
            return new Carwash(this.name, this.type, etc...)
        })
    )
}

Is mapping the only way to navigate through this complexity? Could introducing helper functions help streamline the process of fetching the data? These are questions that linger in my mind.

NOTE: The specific structure of custom objects may not be crucial here. What matters is that they consist of basic data types along with other custom objects.

Answer №1

To achieve this, simply follow these steps:

http.get<CarWash>(this.carwashApiUrl).subscribe((carwash: CarWash) => console.log(carwash));

Ensure that your CarWash class has all the necessary complex properties defined. If you need help with defining these properties, consider using generators like the Swagger Editor to extract them from your API's class definitions. Then, you can proceed to map your CarWashDto to a CarWash object according to the requirements of your UI.

Answer №2

If you require concrete object instances, rather than just a structured object graph defined with the appropriate types, then the following code snippet can be utilized:

Utilizing Object.assign:

  productsClassInstance$ = this.http.get<ProductClass>(this.productsUrl)
    .pipe(
      map(product => Object.assign(new ProductClass(), {
        ...product,
        suppliers: (product.suppliers ? product.suppliers.map(supplier => Object.assign(new SupplierClass(), {
          ...supplier
        })) : [])
      }))
    );

This implementation employs the map operator to transform the response into the desired object hierarchy. It makes use of Object.assign to instantiate a new object of the specified class and the spread operator (...) to replicate the data within that new instance.

Similar code syntax can be applied for each "child" object or array of objects.

[Revised to support multiple levels of objects]

OR Utilizing constructors

productsClassInstanceMultipleLevels2$ = this.http.get<ProductClass[]>(this.productsUrl)
.pipe(
  map(products => products.map(product => 
    new ProductClass(
      product.id, 
      product.productName,
      (product.suppliers ? product.suppliers.map(supplier => new SupplierClass(
        supplier.id,
        supplier.name
      )) : []) ))
  )
);

[Updated once more to utilize constructors instead of Object.assign]

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

Passing a generic type as a parameter in a generic class in TypeScript

TypeScript: I have a method in the DataProvider class called getTableData: public static getTableData<T extends DataObject>(type: { new(): T}): Array<T> { ... } Everything works fine when I use it like this: let speakers = DataProvider.getT ...

The type 'unknown' cannot be assigned to type 'KeyboardEvent'. Error in file 'ts' (2345)

Currently delving into TypeScript and Angular, I encountered an issue in my textbook with an example that refuses to compile. I am unsure of how to resolve this problem. Below is the malfunctioning function: ngOnInit(): void { const logger = fromEvent ...

Checking that an object's keys are all present in an array in TypeScript should be a top priority

I am working with a const object that is used to simulate enums. My goal is to extract the keys from this object and store them in an array. I want TypeScript to generate an error if any of these keys are missing from the array. // Enum definition. export ...

What is the best way to loop through a formarray and assign its values to a different array in TypeScript?

Within my form, I have a FormArray with a string parameter called "Foo". In an attempt to access it, I wrote: let formArray = this.form.get("Foo") as FormArray; let formArrayValues: {Foo: string}[]; //this data will be incorporated into the TypeScript mod ...

Enhance the appearance of a custom checkbox component in Angular

I developed a customized toggle switch for my application and integrated it into various sections. Recently, I decided to rework it as a component. However, I am encountering an issue where the toggle switch button does not update in the view (it remains t ...

Creating an internal link to navigate to a specific element ID within another Angular component

Seeking Solution I am facing a challenge in using an internal link within the <a> tag to navigate to a specific section of another component by HTML ID. My Exploration First, I visited this site: https://www.w3schools.com/html/html_links.asp bu ...

Steps for updating the same array in TypeScript

I have written a reducer code where I check if the same value is already present in the array. If it is, I update the previous value instead of pushing the same value again. Here is my code snippet: export function reducer( state: IDeviceState = ...

The operation of fetching multiple documents within a transaction loop is not functioning as anticipated

I am encountering an issue where I am attempting to retrieve multiple documents within a transaction and then update them all in the same transaction (due to their interdependence). Despite following the rule of ensuring all reads occur before any writes, ...

Issue with SignalR client functionality following update to .NET Core 3.1版本

Upon updating our server-side code to asp.net core 3.1, we encountered an issue with the javascript client for signalr (@microsoft/signalr 3.1.0). The errors we are facing are: https://i.sstatic.net/ITZyK.png Here is the code snippet for the hub initial ...

A guide on efficiently storing and retrieving a webpage containing two angular2 components using local storage

I've been attempting to store and retrieve a page containing two angular2 components from local storage using the following code, but the component CSS is not being applied. Here is the code I'm using to save: localStorage.setItem('pageCon ...

Having trouble with Socket.io sending data to a specific socketId?

I'm currently using Socket.Io 1.7.3 with Angular 2, connecting to a ExpressJS Server. I'm facing an issue where I am unable to send packages to a specific socket ID even though they are a match. Server code snippet: socket.on('subscribeNot ...

Activate a function to focus on an input field once ngIf condition becomes true and the input is revealed

I am currently attempting to focus the cursor on a specific input field that is only displayed when the condition of the surrounding ngIf directive is true. Here is an example of the HTML code structure: <div> <button (click)="showFirst = ...

Encountering issues with Proxy functionality in the latest versions of Angular 13 and Spring Boot

I've encountered an issue with the proxy configuration in Angular. I'm unsure if it's a problem within my Angular settings or if there's a configuration issue in Spring. For testing purposes, I have a backend built in springboot to han ...

Is it possible for a ngrx signal feature store to retrieve the state of the store that it is consuming?

I am interested in creating a feature store that can receive input or reference a state from the store which interacts with or consumes the store, such as the parent of the ngrx signal feature store. For instance, imagine I have a store named "withCounter ...

What is the best way to make the SPA load with the tab displaying the highest value?

I have a React single-page application using Typescript and Material UI. One challenge I'm facing is creating a tab menu with the current month and all previous months, where the last month should be active when the page loads. Despite researching on ...

Testing the Binding of Models in Angular 5 Using Jasmine Framework

I'm currently working on writing a unit test to verify that the JSON data returned from the components method call successfully links to a TypeScript model. Here's what my model looks like: export interface IPlayerAccount { playerId: number; ...

The issue of session type not updating in Next.js 14 with Next-auth 5 (or possibly version 4) is a common concern that needs to

Experimenting with new tools, I encountered an issue when trying to utilize the auth() function to access user data stored within it. TypeScript is indicating that the user does not exist in Session even though I have declared it. Here is my auth.ts file: ...

Is there a disparity in capabilities or drawbacks between ViewChild and Input/Output in Angular?

As I delve into Angular ViewChild and compare it to Input/Output parameters, I can't help but wonder if ViewChild has any drawbacks or limitations compared to Input/Output. It appears that ViewChild is the preferred method, as all parameters are now ...

Best practices for managing backend errors with Next.js 14

Currently, I am developing a project in Next.js 14 and I have set up my API requests using fetch within a handler.tsx file as shown below: async function getPositions() { const response = await fetch( process.env.BASE_API_URL + "/positions?enabl ...

Encountering errors with ng build --prod in Angular 2, but no issues when running ng serve

After attempting to deploy my Angular 2 app to Heroku, I encountered a daunting list of errors when trying to build for production. Interestingly, the app runs smoothly without any issues or errors when using 'ng serve'. You can view the full li ...