26

I have this simple Component

import {Component} from 'angular2/core';
import {RouterLink, RouteParams} from 'angular2/router';
import {Http, Response, Headers} from 'angular2/http';
import {User} from '../../models/user';
import 'rxjs/add/operator/map';


@Component({
    template: `
        <h1>{{user.name}} {{user.surname}}</h1>
    `,
    directives: [RouterLink],
})
export class UserComponent {

    user: User;

    constructor(private routeParams: RouteParams,
        public http: Http) {
            this.user = new User();
            this.http.get('http://localhost:3000/user/' + this.routeParams.get('id'))
                .map((res: Response) => res.json())
                .subscribe((user: User) => this.user = user);
        console.log(this.user);
    }
}

Why does subscribe not cast the response into a full User object. When I am logging the user variable my console say User {_id: undefined, name: undefined, surname: undefined, email: undefined}. But nevertheless binding to .name and .surname in the view is working..

What happens here? Where is the user actually stored?

2

3 Answers 3

25

Good practice is to consume data from GET response using

Observable<Model>

(regarding to Angular documentation https://angular.io/guide/http) so...

// imports

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

// in constructor parameter list

private http: HttpClient

// service method

getUser(): Observable<User> {return this.http.get<User>({url}, {options});}

You do not need to do anything more. I consider this approach as most friendly.

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

8 Comments

This will work for most approaches, but it doesn't cast, more like a type assertion. If you have a function in your class export class User { test() { console.log("works"); } } You can't run user.test().
Just to avoid confusion for anyone this approach is only available in Angular 4.3+ where the new HttpClient was introduced.
@DarrenLewis , what is approach we have to use if get method wants to return array of Model?
@Anil This method works fine also for an array of Model. Just instead of User you put User[]
The author's problem is that his log command is executed before the response arrives. Your answer isn't related to the author's problem at all in my opionion. Furthermore he asks for a "full User object" which I interpret as User class instance. So even if his log was at the correct possition your answer would be wrong as it will return a plain object (as ovmcit5eoivc4 pointed out above)
|
22

Found a solution here: https://stackoverflow.com/a/29759472/2854890

My Method now looks like this:

constructor(private routeParams: RouteParams,
    public http: Http) {
    this.user = new User();
    this.http.get('http://localhost:3000/user/' + this.routeParams.get('id'))
        .map((res: Response) => res.json())
        .subscribe((json: Object) => {
            this.user = new User().fromJSON(json);
        });
}

I enhanced the Serializable by returning the object in the end, so I can leave out something like

var u = new User();
u.fromJSON(...);

and just write

new User().fromJSON(json);

Serializable class

export class Serializable {

    fromJSON(json) {
        for (var propName in json)
            this[propName] = json[propName];
        return this;
    }

}

4 Comments

In Angular 6 that map() line is giving error: Argument of type '(res: Response) => Promise<any>' is not assignable to parameter of type '(value: Response, index: number) => Promise<any>'.
map has changed in Angular 6. Check this out: academind.com/learn/angular/snippets/…
@DanielHitzel Your original problem was that your console.log is executed before the response arrived. Even if it was at the correct position, you would overwrite the value in your subscribe handler by a plain object (this.user = user) which is not an instance of User anymore (Nevertheless a User-compatible object with same properties). Both problems are solved by this answer. However, I wanted to point out that your actual problem - property values are undefined - originated in another mistake.
looking at the code with more experience, yes that's seems to be true :D
0

That's not supported by TypeScript.

See How do I cast a JSON object to a typescript class for more details.

4 Comments

But why am I able to bind to the data anyway. It gets displayed, whereas the variable I am binding to is logged as undefined.. How does binding work then
JSON and object is interchangeable in TypeScript/JS but the created object will only contain what the JSON contained, but no methods or other fields that are only defined in the prototype of of the real class.
if I change this to .subscribe(data => this.user = new User(data._id, data.name, data.surname, data.email)); the log is still the same. What do I need to log in order to get the object the view in binding to?
There is another mistake I missed. ` console.log(this.user);` is executed before the HTTP request is made. this.http.get() is async, which means the task is enqueued in the browser event queue for later execution, then JS execution is continued (with your log command). When the HTTP call is done, the callback passed to .subscribe(...) is executed, but that's much later. Move the log call into subscribe((user: User) => { ... });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.