Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 define json observable return type to interface or class model

I wish to have my json api response to be hydrated into a class or based on a interface so I always know what attributes I have. I have the following JSON:

{
  "users": [
    {
      "id": "bd3d70fd-03f7-4f5e-9ac1-4cb7221e352d",
      "username": "caroga",
      "username_canonical": "caroga",
      "email": "[email protected]",
      "email_canonical": "[email protected]",
      "groups": [],
      "roles": [],
      "games": [],
      "_links": {
        "self": {
          "href": "/app_dev.php/api/users/bd3d70fd-03f7-4f5e-9ac1-4cb7221e352d"
        },
        "users": {
          "href": "/app_dev.php/api/users/"
        }
      }
    },
    {
      "id": "df33d9cb-b575-427f-b2bd-ed9c364110f7",
      "username": "joemi",
      "username_canonical": "joemi",
      "email": "[email protected]",
      "email_canonical": "[email protected]",
      "roles": [],
      "games": [],
      "_links": {
        "self": {
          "href": "/app_dev.php/api/users/df33d9cb-b575-427f-b2bd-ed9c364110f7"
        },
        "users": {
          "href": "/app_dev.php/api/users/"
        }
      }
    }
  ],
  "count": 2
}

And the following interface and model:

Users.ts

import {User} from "../User";
export interface Users{
    count: number,
    users: Array<User>,
}

User.ts

export class User {
    id: string;
    username: string;
    username_canonical: string;
    email: string;
    email_canonical: string;
    groups: Array<string>;
    roles: Array<string>;
    games: Array<string>;

    constructor(values: Object = {}) {
        Object.assign(this, values);
    }
}

user-data.service.ts

import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
import {ApiService} from "./api.service";
import {environment} from "../../environments/environment";
import {Observable} from "rxjs";
import {Users} from "../Models/Interfaces/Users";

@Injectable()
export class UserDataService extends ApiService {

    constructor(private http: Http) {
        super();
    }

    getAllPlayers(): Observable<Users> {
        return this.http.get(environment.apiUrl + '/users/', this.addJwtHeader())
            .map(result => result.json());
    }
}

users.component.ts

import {Component, OnInit} from "@angular/core";
import {UserDataService} from "../../services/user-data.service";
import {User} from "../../Models/User";
import {Observable} from "rxjs";
import {Users} from "../../Models/Interfaces/Users";

@Component({
    selector: 'app-users',
    templateUrl: './users.component.html',
    styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {

    private users: User[];

    constructor(private PlayerDataService: UserDataService) {
    }

    ngOnInit(): void {
        this.getListOfAllUsers().subscribe(users => {
            console.log(users);
            this.users = users.users;
        });
    }

    public getListOfAllUsers(): Observable<Users> {
        return this.PlayerDataService.getAllPlayers();
    }
}

users.component.html

<ul>
    <li *ngFor="let user of users">
        {{ user.username }}
    </li>
</ul>

The issue at hand: Although I am getting results on screen, it's still using the json parameters instead of the ones defined in the model. I noticed this as changing the username attribute in User.ts doesn't throw an error or even reflect in my object in the console. Basically I expected that private users: User[]; would be a array of User objects, but it's not. Console log of response array

What am I doing wrong here?

like image 510
Roberto Avatar asked Mar 10 '23 17:03

Roberto


1 Answers

getAllPlayers(): Observable<Users> {
    return this.http.get(environment.apiUrl + '/users/', this.addJwtHeader())
        .map(result => result.json());
}

You're basically "casting" (or simply "referring") a simple JS object (which is returned from the json() function into what you expect to be a User, but it doesn't work that way. TypeScript only assists you with keeping track of everything, but when casting an object to a class, TypeScript can only do so much, you can't really take an object and cast it to an instance of a class, that's just not how OOP works.

I'm guessing what you want to do is maybe something like this:

getAllPlayers(): Observable<Users> {
    return this.http.get(environment.apiUrl + '/users/', this.addJwtHeader())
        .map(result => result.json().map(obj => new User(obj)));
}

(Thanks to your constructor of course)

like image 88
Amit Avatar answered Apr 28 '23 22:04

Amit