Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular HttpClient is not returning quite the objects I expect

Lets say I have

class Foo {
    bar: string;

    public doSomething() {}

}

In my service I'm getting array of those from a server:

this.http.get<Foo[]>(...)

This works, I'm getting all the expected values. BUT when I take one of the objects I got and try to invoke doSomething() on it, I get an error saying that foo (which is of type Foo) doesn't have method doSomething().

It appears that while http.get correctly parsed JSON and assigned all the properties of my objects, it didn't bother with setting up proper prototypes for them, so essentially they are Foos in name only, they are missing all the methods of Foo.

Is that normal or am I doing something wrong? Thank you.

like image 648
Viktor A Avatar asked May 03 '18 23:05

Viktor A


1 Answers

Effectively. In the end, you're just parsing JSON, and the result of parsing JSON are just plain JavaScript objects, never custom classes. this.http.get<Foo[]> is just a hint for the compiler, but the generic parameter does not really do anything. It doesn't convert one object in another, not does it give them types.

As an advice, you should never use classes when typecasting the result of calls to services or getting objects from localStorage, sessionStorage and such. You should use interfaces instead, without methods.

However, it is possible to achieve what you want, if you really need your objects to be of class Foo and have the doSomething method:

this.http.get<Foo[]>(...).do(items => items.forEach(item => Object.setPrototypeOf(item, Foo.prototype)));

This will give every object the correcto prototype. However, this has a performance penalty, as changing the prototype of created objects messes with browser optimization, so do it under your own risk.

Another option would be to give Foo a constructor:

class Foo {
    constructor(item: Foo) {
        Object.assign(this, item);
    }

    bar: string;

    public doSomething() {}
}

Now:

this.http.get<Foo[]>(...).pipe(map(items => items.map(item => new Foo(item)))).subscribe(...);
like image 144
Oscar Paz Avatar answered Nov 07 '22 06:11

Oscar Paz