Leveraging Classes for Http Observables in Angular 12

Utilizing the power of Angular 12

The backend response received from the HTTP service is structured as follows:

Array<{
   id: string;
   title: string;
   created: string;
}>

// Service
public list(): Observable<Array<Item>> {
  return this.http.get<Array<Item>>(url);
}

// Component
this.service.list().subscribe(res => {
  this.item = res;
});

Implemented a model class to properly typecast the response data

export class Item {
  id: string;
  title: string;
  created: string;

  get parsedTitle() {
    const t = this.title;
    // perform some parsing on title
    return t;
  }
}

When attempting to display the parsed title using parsedTitle in the HTML, no output is generated.

Experimented with changing the getter to a function public parsedTitle(){}, but encountered an error stating parsedTitle is not a function.

How can I successfully convert the returned observable into instances of class objects from an array of objects?

Answer №1

To convert the plain objects to instances of the actual Item class, you can follow this approach:

// Define the Item class
class Item {
    constructor(item: Partial<Item>) {
        Object.assign(this, item);
    }

    id: string = '';
    title: string = '';
    created: string = '';

    get parsedTitle() {
        const t = this.title;
        // Perform some parsing on the title
        return t;
    }
}

// Implement the service method
public getList(): Observable<Array<Item>> {
  return this.http.get<Array<Item>>(url).pipe(
    map(items => items.map(item => new Item(item))),
  );
}

Answer №2

Consider the approach of utilizing classes in various programming languages. Typically, you must create an object of that particular type by invoking the class constructor to be able to employ your methods (unless they are static functions).

When adapting this concept to your scenario, avoid simply instructing TypeScript to recognize the objects fetched from the API as instances of Item. Instead, construct new Item instances using these objects.

Add a constructor to your Item class that takes a parameter and copies all properties found in that object into this (the current instance).

export class Item {
  id: string;
  title: string;
  created: string;

  constructor(item?: Item) {
    Object.assign(this, item ?? {});
  }

  get parsedTitle() {
    const t = this.title;
    // perform some parsing on title
    return t;
  }
}

In your service class, ensure that you convert the objects retrieved from the API into a legitimate array consisting of instances of type Item in the following manner:

// Service
public list(): Observable<Array<Item>> {
  return this.http.get<Array<Item>>(url).pipe(
    map(item => new Item(item))
  );
}

Answer №3

In the scenario you described, the issue arises from the fact that the object received from the backend (in this case, in JSON format) is being serialized to the target type (an instance of `Item`) without being initialized using its constructor.

To resolve this issue and successfully achieve your objective, each `object` needs to be initialized by creating a new instance of `Item` using the `new Item()` constructor.

You can implement this by following the example below:

item.model.ts:

export class Item {
  id: string;
  title: string;
  created: string;

  get parsedTitle() {
    const t = this.title;
    // perform necessary parsing on the title
    return t;
  }

  constructor(item: Partial<Item>) {
    Object.assign(this, item);
  }
}

items.service.ts:

// Service
public list(): Observable<Array<Item>> {
  return this.http
    .get<Array<Item>>(url)
    .pipe(map(items => items.map(item => new Item(item))));
}

Answer №4

The main issue at hand is that you are failing to instantiate the Item class and assign the API response to it. The conversion from the response to the Item class needs to be handled explicitly as it won't occur automatically. In your current code, an attempt is made to access title on this object where this is not available since it has not been instantiated.

Make a slight adjustment in the code like below:

// Service

public list(): Observable<Array<Item>> {
  return this.http.get<Array<Item>>(url)
         .pipe(map((items) => (items.map((item) => (new Item(item))))));
}

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

Turning XSD into TypeScript code

Stumbling upon this tool called CXSD, I was intrigued. The documentation describes cxsd as a streaming XSD parser and XML parser generator designed for Node.js and TypeScript (optional but highly recommended). It seemed like the perfect solution for my ne ...

Exploring the Incorporation of String as a Component Identifier in React and TypeScript

My input component can render either a textarea component (from a library) or a regular input. Check out the code below: import React, { useEffect, useRef, useState } from 'react' import './AppInput.css' interface Props { placehold ...

In TypeScript, there is a mismatch between the function return type

I am new to TypeScript and trying to follow its recommendations, but I am having trouble understanding this particular issue. https://i.stack.imgur.com/fYQmQ.png After reading the definition of type EffectCallback, which is a function returning void, I t ...

When using React MUI Autocomplete, make sure to handle the error that occurs when trying to filter options using the

I am trying to implement an autocomplete search bar that makes a custom call to the backend to search through a list of tickers. <Autocomplete multiple id="checkboxes-tags-demo" options={watchlis ...

JavaScript's async function has the capability to halt execution on its own accord

Presented here is a JavaScript async function designed to populate a sudoku board with numbers, essentially solving the puzzle. To enhance the user experience and showcase the recursion and backtracking algorithm in action, a sleeper function is utilized b ...

The function that was provided as a parameter is not functioning as expected

I have a WsConnector class responsible for connecting to my backend application and subscribing to a WebSocket topic. export class WsConnector { private stompClient: any; connect(onMessage: (msg) => void) { this.stompClient = Stomp.over ...

Steps to arrange by the number of rows in ag grid

I've been experimenting with the rows group feature in ag-grid, But I'm curious if it's feasible to sort the group column based on the number of rows within each group? Here is an example of what I am trying to achieve: https://i.sstatic. ...

Tips for successfully clearing the localStorage in an Ionic2 application upon exiting

Could someone please provide guidance on how to detect when the application is being exited using the hardware back button and then clear my localStorage data? I have three main reasons for needing this functionality: 1. Prompt the user to confirm if they ...

When working with multiple charts on Angular ChartJs, the data may not display properly

My goal is to display multiple Charts in a single page using Angular. I came across an Example that uses ViewChildren: const baseConfig: Chart.ChartConfiguration = { type: 'pie', options: { responsive: true, } }; @ViewChildren('c ...

Explanation of TypeScript typings for JavaScript libraries API

Currently, I am in the process of building an Express.js application using TypeScript. By installing @types and referring to various resources, I managed to create a functional program. However, my main query revolves around locating comprehensive document ...

Incorporate a fresh attribute to the JSON data in an Angular API response

I'm currently working on updating my JSON response by adding a new object property. Below is an example of my initial JSON response: { "products": [{ "id": 1, "name": "xyz" }] } My goal is to include a new object property ca ...

Removing item from Angular service

Within my Angular 2 application, I have created a service called events.service.ts: const EVENTS = { 1512205360: { event: 'foo' }, 1511208360: { event: 'bar' } } @Injectable() export class EventsService { getEvents() ...

Incorporating a TypeScript module into a JavaScript module within a React application

I'm encountering an issue with my React app that was created using create-react-app. I recently added a Typescript module to the project, which is necessary for functionality reasons. Although it will remain in Typescript, I made sure to install all t ...

Mismatch between generic types

When working with this code, I encounter a syntax error at m1 and m2. The error message states: Type 'T' is not assignable to Type 'boolean' or Type 'T' is not assignable to Type 'string' interface customMethod { ...

Having trouble with ng build optimization error in Angular?

While developing a real-time chat application using Angular 13, I encountered an issue when trying to build the app for testing on my Node.js web server: ng build Every time I run this command, I receive an error in the console. Here is a screenshot of ...

Using Highcharts to dynamically color a map based on data values in a React TypeScript project

I'm attempting to generate a map where each country is filled with colors based on its specific data, similar to the example shown in this map. I am looking for a functionality akin to the use of the formatter function within the tooltip. I have expe ...

Guide on importing videojs-offset library

I am working on a component that utilizes video.js and HLS streaming in Angular. The component code is as follows: import { Component, ElementRef, AfterViewInit, ViewChild, Input, EventEmitter, Output } from '@angular/core'; import ...

Tips for dynamically inserting latitude and longitude into an iframe map URL using the Angular ngFor directive

I'm currently working on an application that is designed to load images, data, latitude, and longitude from a server. I have successfully parsed the JSON data and displayed it in HTML. Everything seems to be functioning correctly. However, I am facing ...

Ways to attach a label to router-outlet

Is there a way to dynamically generate router-outlet elements with different names like the following? <router-outlet name='chart.id'></router-outlet> For example, if chart.id is "id1, id2, id3", how can I generate these at runtime ...

Ways to sort through a Unix timestamp

Recently, I encountered an issue with my Ionic mobile application. It has a search button and filter feature that works well for filtering words. However, the problem arises when it comes to filtering dates displayed in the app as timestamps using Angular ...