无法将 Angular5 HttpClient 响应映射到我的打字稿 类

Can't map Angular5 HttpClient responses to my typescript classes

我在将我的 Angular 5 HttpClient 响应映射到具体的 TypeScript classes 时遇到问题。

我有一个关注 Angular 5 class:

export class MaintainableUser {

  constructor(public id: string, public name: string, public privileges: string[]) {
  }

  public hasPrivilege(privilege: string): boolean {
    return this.privileges.indexOf(privilege) != -1;
  }

  public togglePrivilege(privilege: string) {
    if (this.hasPrivilege(privilege)) {
      this.privileges.splice(this.privileges.indexOf(privilege), 1);
    } else {
      this.privileges.push(privilege);
    }
    console.log(this.privileges);
  }
}

我有以下 Angular5 项服务:

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {MaintainableUser} from './maintain-users/maintainable-user';
import {Observable} from 'rxjs/Observable';
import {catchError} from 'rxjs/operators';
import {ApiErrorHandler} from '../api-error-handler';

@Injectable()
export class AdminService {

  constructor(private http: HttpClient, private errors: ApiErrorHandler) {
  }

  getAllUsers(): Observable<MaintainableUser[]> {
    return this.http.get<MaintainableUser[]>('api/admin/users').pipe(catchError(this.errors.handle));
  }

}

这很好很干净,现在我可以像这样使用我的服务了:

import {Component, OnInit} from '@angular/core';
import {MaintainableUser} from './maintainable-user';
import {AdminService} from '../admin.service';

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

  constructor(private adminService: AdminService) {
  }

  ngOnInit() {
    this.reloadMaintainableUsers();
  }

  private reloadMaintainableUsers() {
    this.adminService.getAllMaintainableUsers().subscribe(
      data => {
        console.log(data);
        console.log(data[0] instanceof MaintainableUser);
        data[0].hasPrivilege('ADMIN');
      },
      err => {
        console.log('Service error: ' + err);
      }
    );
  }

}

问题是我收到的数据实际上不是MaintainableUser的列表。来自我的 MaintainableUsersComponent 的响应块具有以下输出:

{"id": "john", "name": "John Doe", "privileges": ["ADMIN"]}

false

ERROR TypeError: _v.context.$implicit.hasPrivilege is not a function

这是为什么?为什么我的数据 [0] 不是 instanceof MaintainableUser?我是 Angular 的新手,但我从不同的教程中了解到,从第 4 版开始,我应该能够完全像那样自动映射到我的 classes/interfaces。或者它只是一个假的东西,所以你可以安全地确定你的对象是强类型的,但你不能确定它是实际对象本身的实例吗?如果我可以在我的响应 classes 中包含一些辅助方法,那将会非常好,这样我就可以在视图中使用它们,但目前我还没有找到一种干净的方法来做到这一点。

instanceof 检查原型链,因此除非您的 getAllMaintainableUsers() 正在创建新的 MaintainableUser 对象,否则它将 return 为假。我的猜测(根据您上面的 getAllUsers() 调用)是您直接 return 从服务中获取一个对象。即使您将类型注释为 MaintainableUser[],因为它不是使用 MaintainableUser 的原型创建的,instanceof 也不会 return 对于 data[0] instanceof MaintainableUser.

为真

你的 MaintainableUser type 映射得很好,所以 HttpResponse 正确地映射了泛型类型,Typescript 编译器没有抱怨,但你的回应是不是 MaintainableUser 实例 。它仍然只是一个从 JSON.

解析出来的对象

您的 MaintainableUser class 有一个方法 属性,相当于将该方法添加到 MaintainableUser.prototype。事实上,当针对 ES5 时,这正是 Typescript 编译器所做的。

请记住,Typescript 类型对转译的 Javascript 没有 影响。将 MaintainableUser[] 类型传递给 HttpClient 只是将强类型添加到响应中。本质上,您只是告诉您的 IDE 和编译器 Response 是 MaintainableUser[],但它实际上并不是 MaintainableUser 实例 。您需要使用 new 关键字来传递该原型。

我将重构 MaintainableUser 以接受该 Response 作为构造函数参数并将响应映射到 MaintainableUser 的新实例,或者将 hasPrivelage 方法移动到服务。

理解 classes/objects 的工作原理是相当困难的。从 API 返回的只是一个普通对象(已解析 JSON),它没有 MaintainableUser 的任何方法,因为它不是您的 class 的实例。它仅具有与您的 class.

相同的属性

您需要手动将值从对象传递到构造函数并创建 class 的实例。 Angular 不会自动执行。我经常这样做:

export class MaintainableUser {

  public static createInstance({ id, name, privileges }): MaintainableUser {
    return new MaintainableUser(id, name, privileges);
  }

  constructor(public id: string, public name: string, public privileges: string[]) {}

  public hasPrivilege(privilege: string): boolean {
    return this.privileges.indexOf(privilege) != -1;
  }

  public togglePrivilege(privilege: string) {
    if (this.hasPrivilege(privilege)) {
      this.privileges.splice(this.privileges.indexOf(privilege), 1);
    } else {
      this.privileges.push(privilege);
    }
    console.log(this.privileges);
  }
}

然后:

getAllUsers(): Observable<MaintainableUser[]> {
  return this.http.get<MaintainableUser[]>.('api/admin/users').pipe(
    map(MaintainableUser.createInstance)
    catchError(this.errors.handle)
 );
}