17

I have a question about the Angular 5 httpClient.

This is a model class with a method foo() I'd like to receive from the server

export class MyClass implements Deserializable{
  id: number;
  title: string;

  deserialize(input: any) {
    Object.assign(this, input);
    return this;
  }

  foo(): string {
    // return "some string conversion" with this.title
  }
}

This is my service requesting it:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { MyClass } from './MyClass';

@Injectable({
  providedIn: 'root',
})
export class MyClassService {

  constructor(private http: HttpClient) {
  }

  getMyStuff(): Observable<MyClass[]> {
    // this is where I hope to convert the json to instances of MyClass
    return this.http.get<MyClass[]>('api/stuff')
  }
}

My Problem

When I ask the service for instances of MyClass I get the data, but I cannot run {{ item.foo() }} in the template. Also, when I console.log() the typeof of an item where it is received in the service, I do no see instances of an object of MyClass.

What am I doing wrong? I thought that writing this.http.get<MyClass[]>('api/stuff') would do the conversion.

Any hints? Thank you in advance!

3

2 Answers 2

33

When doing that, TypeScript only does "type assertion". It means that you're telling TypeScript that your object is of type MyClass, but the object isn't actually an instance of MyClass at runtime. In order to call functions defined in your model object, you have to define constructors in your model classes like that :

constructor(obj?: any) {
    Object.assign(this, obj);
}

Then in your services add a mapping like this :

http.get<MyClass>('/my-class').pipe(
      map(res => new MyClass(res))

Note: the code above is RxJS 6 style, i don't know which version you are using

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks to you I reread this tutorial that I did not do to the end. What I now do in the service is this: `return stuffObjects.map(stuff => new MyClass().deserialize(stuff));
You're welcome, I personnally like the constructor way, the Deserialize interface is another suitable option
this will only work when MyClass does not have complex members (eg members of type Date or other classes)
@Jota.Toledo yes, and this can be solved by defining setters for those properties
-2

It works for me like this

import { HttpClient } from '@angular/common/http';

...
this.httpClient.get<MyResponse>('http://......').toPromise()
      .then((myResponse) => {
        console.log('myResponse.myField: ' + JSON.stringify(tokenResponse));
      })
      .catch((error) => {
        console.log('Promise rejected with ' + JSON.stringify(error));
      });

...
interface MyResponse {
  myField: string;
  myOtherField: string;
}

3 Comments

Please add some explanation to your answer such that others can learn from it
Please read the question again, it's about calling functions on a business class hydrated from the backend, how an interface solves this problem?
@GuerricP this question is about deserializing server response into a model and this is exactly what my code does.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.