7

As I'm learning Angular 2 I used an observable to fetch some data via an API. Like this:

getPosts() {
        return this.http.get(this._postsUrl)
            .map(res => <Post[]>res.json())
            .catch(this.handleError);
    }

My post model looks is this:

export class Post {

constructor(
    public title: string,
    public content: string,
    public img: string = 'test') {
}

The problem I'm facing is that the map operator doesn't do anything with the Post model. For example, I tried setting a default value for the img value but in the view post.img displays nothing. I even changed Post[] with an other model (Message[]) and the behaviour doesn't change. Can anybody explain this behaviour?

1
  • What doesn't make sense to me is why Typescript doesn't throw an error when the cast fails or when the generic object is then passed to a function that requires a Post type? Does Typescript type checking just not work? The generic object can have random properties that don't align to the typed parameter so implicit casting can't be happening, yet generic object is passed without issue. Commented Jul 1, 2017 at 0:57

2 Answers 2

14

I had a similar issue when I wanted to use a computed property in a template.

I found a good solution in this article:

http://chariotsolutions.com/blog/post/angular-2-beta-0-somnambulant-inauguration-lands-small-app-rxjs-typescript/

You create a static method on your model that takes an array of objects and then call that method from the mapping function. In the static method you can then either call the constructor you've already defined or use a copy constructor:

Mapping Method

getPosts() {
  return this.http.get(this._postsUrl)
    .map(res => Post.fromJSONArray(res.json()))
    .catch(this.handleError);
}

Existing Constructor

export class Post {
  // Existing constructor.
  constructor(public title:string, public content:string, public img:string = 'test') {}

  // New static method.
  static fromJSONArray(array: Array<Object>): Post[] {
    return array.map(obj => new Post(obj['title'], obj['content'], obj['img']));
  }
}

Copy Constructor

export class Post {
  title:string;
  content:string;
  img:string;

  // Copy constructor.
  constructor(obj: Object) {
    this.title = obj['title'];
    this.content = obj['content'];
    this.img = obj['img'] || 'test';
  }

  // New static method.
  static fromJSONArray(array: Array<Object>): Post[] {
    return array.map(obj => new Post(obj);
  }
}

If you're using an editor that supports code completion, you can change the type of the obj and array parameters to Post:

export class Post {
  title:string;
  content:string;
  img:string;

  // Copy constructor.
  constructor(obj: Post) {
    this.title = obj.title;
    this.content = obj.content;
    this.img = obj.img || 'test';
  }

  // New static method.
  static fromJSONArray(array: Array<Post>): Post[] {
    return array.map(obj => new Post(obj);
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, looks like a decent solution! I'm gonna wait a few more days for maybe some other answers, otherwise I'll mark yours as accepted!
Good work around. unfortunately requires turning off "noImplicitAny" setting in tsconfig if you wanted it on.
0

You can use the as keyword to de-serialize the JSON to your object.

The Angular2 docs have a tutorial that walks you through this. However in short...

Model:

export class Hero {
  id: number;
  name: string;
}

Service:

...
import { Hero } from './hero';

...
get(): Observable<Hero> {
    return this.http
               .get('/myhero.json')
               .map((r: Response) => r.json() as Hero);
}

Component:

get(id: string) {
    this.myService.get()
      .subscribe(
        hero => {
          console.log(hero);
        },
        error => console.log(error)
      );
}

2 Comments

Does it works if we have different parameter names for class and json data? i.e if class have name and we get user_name from json.
Well, I have to say that it doesn't answer the question at all. Basically, the as syntax (that you suggested) works exactly the same way as <...>. syntax. The only difference is that the <...> syntax conflicts with (.tsx files - JSX syntax), as explained in this question. Also, neither <...>, nor as will work in this case. The above answer is the correct one.

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.