Children subscribing to the observable class do not inherit any parent fields

My current situation involves an observable that retrieves data from a server. This data is stored in the public fields of a DocGroup object.

export class DocGroup extends Selectable {
  public uuid;
  public name;
  public set_uuid;
  public order;
  public strings;
}

To add extra functionality to this object, I have extended it with a class named Selectable. This extension allows my object to be selected for further processing.

export class Selectable {

  public readonly isSelectable = true;
  public selection_parent: Selectable;
  protected _selected: number = -1;

  public select(state?: boolean) {
    // Implementation here
  }

  // Additional methods go here 
}

The issue arises when parsing this object with JSON as it lacks parent fields and methods despite its specified type. When attempting to call its select() method, an exception "group.select is not a function" is thrown. The object is retrieved using the following method:

public getAll(contract: Contract): Observable<DocGroup[]> {
  const url = this.baseUrl + 'getAll';
  const response: Observable<DocGroup[]> = this.http.post<DocGroup[]>(url,
    {"contract_uuid": contract.uuid}, this.httpOptions);
  return response;
}

After inspecting the printed object in the console, it became apparent that it was missing parent fields as well:

{
  "uuid":"3fe5329a-888c-4a59-e99b-aa5b8c5791dc",
  "set_uuid":"6c8d33c3-c147-4d36-db0d-e41f7d9b87b3",
  "order": null,
  "name":"Group 1"
}

How can I ensure that my HTTP-retrieved object contains all necessary methods and fields? Could making Selectable an interface instead help resolve this issue caused by potentially breaking design patterns?

I have attempted using Object.assign() to adjust the object, but it only populated missing fields, not methods.

Answer №1

When working with http operations, the data is shaped to fit the defined interface. However, it does not automatically create instances of the defined type. This means that the fields are not validated against the class structure, allowing for extra or incorrect data to be included without errors.

If you want to manipulate the returned data as instances, you will need to manually create them from the reshaped data provided.

For instance:

// Transformation into a class instance
this.http.get<ProductClass[]>(this.productsUrl)
  .pipe(
    tap(data => console.log('Before mapping', JSON.stringify(data))),
    map(products => products.map(product => {
      const productInstance: ProductClass = Object.assign(new ProductClass(), {
        ...product,
        price: product.price * 1.5,
        searchKey: [product.category]
      });
      productInstance.inventoryValuation = productInstance.calculateValuation();
      return productInstance;
    })),
    tap(data => console.log('After mapping', JSON.stringify(data))),
    catchError(this.handleError)
  );

The code above utilizes the map operator from RxJS to transform the array of products and iterate through each one.

In the map operator, the first line creates a new instance of the class using Object.assign. By starting with a new instance and spreading the properties of the product, we can generate an actual instance.

Object.assign(new ProductClass(), { ...product } )

In this example, additional fields like price and searchKey are added as per requirements. These modifications may vary depending on your specific needs.

The instance created allows us to invoke methods (such as calculateValuation) on the object, showcasing the functionality of having an instance available.

To adapt this code for your project, consider a similar approach:

Object.assign(new DocGroup(), { ...item} )

Answer №2

When data is serialized and deserialized using JSON (or any other format), all type and class information gets lost in the process.

Unfortunately, TypeScript does not have a built-in feature that can automatically convert a plain JavaScript object into a class object.

However, there are third-party modules available for this purpose. One highly recommended option is the class-transformer module (https://github.com/typestack/class-transformer).

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

Ensure that an Enum is limited to only numerical values (implement type checks for Enum)

I am looking to enforce a Generic type to be strictly a numerical enum. This means that the values of the Enum should only be numbers. Here is an example: export type TNumberEnum<Enum> = { [key in keyof Enum]: number }; enum sample { AA, BB ...

The attribute 'use' is not found within the data type 'typeof...', and the property 'extend' is not present within the data type 'typeof'

As I embark on building my very first Vue app using TypeScript, I find myself facing a frustrating issue: Property 'xxx' does not exist on type 'typeof. Despite my efforts to research similar problems, none of the suggested solutions have pr ...

How to modify a specific property of an array object in JavaScript

I have an array of objects that looks like this: [ { number: 1, name: "A" }, { number: 2, name: "e", }, { number: 3, name: "EE", } ] I am looking for a way to insert an object into the array at a specific position and ...

Difficulty with setting up Typescript in Visual Studio Code on MacOS Catalina

I'm currently facing an issue that appears to be related to the environment. How can I resolve it? And how do I link the installed TSC to my console? Steps to Recreate: npm install -g typescript was able to successfully install or update [email ...

What is the maximum number of groupings that can be created from a set of numbers within a

I'm trying to figure out how to handle a specific task, but I'm running into some obstacles. When adding numbers to clusters, a number is considered to belong to a cluster if its distance to at least one existing number in the cluster is within a ...

Tips for organizing JSON data from a multiselect form

I am currently working on a template driven form with a multiselect field named assets. My framework of choice is semantic UI. <div ngModelGroup="assets"> <div class="field"> <label for="resourceName">Assets</label ...

Angular 2+ seems to be failing to detect and update changes in variables within the template

I have a component that includes rendering the user's name from the profile object. The corresponding part of the template looks like this: <button mat-button [matMenuTriggerFor]="userMenu" *ngIf="isAuthenticated()"> {{profile?.name} ...

Executing multiple insert statements using the executeSql method

I am encountering an issue with multiple inserts in my code. Here is the snippet: db.openDatabase({ name: "data.db", location: "default" }).then(() => { db.executeSql( +"INSERT INTO check_item (name) VALUES ('Pneus - ...

Retrieve an item from an array using a Select component

Is there a way to retrieve the complete object representation of an item from a list in React? Currently, when I select an item and call handleChangeSelectAuto, the original value from useState is returned instead of the entire object. How can I ensure tha ...

What kind of function am I using and passing as props in React when working with TypeScript?

I recently developed a customized Checkbox component. The TypeScript setup in my project doesn't allow the use of any type, so I'm struggling to define the specific type for the handleCheckbox() function (found within the FilterBox component) th ...

Upon successfully logging into the app, Azure AD B2C integrated with MSAL and Angular will automatically redirect users to the login page

I recently updated my Angular app to make it accessible to customers by switching from ADAL to MSAL for authentication. I configured the app with Azure AD B2C credentials and everything seems to be working smoothly, except for one issue. When I try to logi ...

Disappearing Ionic React useState value issue encountered when passing it as a prop parameter in a function

After transitioning from JavaScript to TypeScript, I encountered an issue with my useState hook not printing anything when used in a parent component. My confusion also extends to importing types in TypeScript. interface Props { sendTextMessage: (text? ...

Tips for picking out a particular item from a list of child elements

When I select the first parent's children array, it ends up selecting every other parent's children as well. This is due to index 0 remaining the same for all of them. How can I specify and highlight a specific child? Link: Check out the stackb ...

Encountering an issue with Angular 2's Angular Material: Uncaught TypeError - Cannot read property 'sdeptname' of undefined

I recently set up a basic table in Angular 2 using angular material. I have included two mat-tables where selected rows from the first table can be moved to the second table by clicking Move To Table 2, and vice versa by clicking Move To Table 1. However, ...

A simple way to automatically fill an input field with a mask when clicking in Angular 2

When a user clicks on this span, the following action is triggered: <span data-content="15" #Fast15 (click)="enterFastTime(Fast15)" class="quick-time">15mins</span> Users can also manually input a date in the following input field. If they ...

The Angular-slickgrid is encountering an issue where it is unable to access the property "hostView" as it

Hey there developers, I've been experimenting with the angular slickgrid library and attempting to incorporate the rowDetailView feature it offers. The issue I'm facing is that while I can expand the view by clicking on it, I'm unable to c ...

Submitting sizable tiff documents using nodejs

I'm currently working on a web application with MEAN Stack and Angular 6. My challenge is uploading tiff files (maps) with large file sizes, up to 1.4 GB. I've tried using 'ng2fileUpload' and 'multer', but they are not compati ...

Currently leveraging the TSL Mastodon API, I developed a basic function designed to extract images from a specified URL and post them on Mastodon. However, the outcomes I am receiving are not

This is the code block responsible for grabbing and uploading the image async MediaUpload(photos : any[]) { const client = await this.Login() const full_photo_urls : string[] = photos.map((item) => item.full) let image_ids : string[] = [ ...

Best practices for transferring objects between components while navigating routes in Angular

I need some advice on handling a specific component in my project. Here is the code snippet: <div class="animal"> <a routerLink="/animal/{{animal.id}}"> <div> ... </div> </a> </div> This component receives ...

Is there a way to divide the array based on a specific letter in the alphabet using Angular?

I am trying to create something similar to this: "ABCDEF", "GHIJK", "LMNO", "PRSTU", "VYZXWQ", "0123456789" I have a list in alphabetical order; I want names starting with "ABCDEF" to be in one a ...