【问题标题】:Can't map Angular5 HttpClient responses to my typescript classes无法将 Angular5 HttpClient 响应映射到我的打字稿类
【发布时间】:2018-09-05 02:21:47
【问题描述】:

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

我有以下 Angular 5 类:

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"]}

ERROR TypeError: _v.context.$implicit.hasPrivilege 不是函数

这是为什么呢?为什么我的数据 [0] 不是可维护用户的实例?我对 Angular 很陌生,但我从不同的教程中了解到,从第 4 版开始,我应该能够像这样自动映射到我的类/接口。或者它只是一个假的东西,所以你可以确保你的对象是强类型的,但你不能确定它是实际对象本身的实例?如果我可以在我的响应类中有一些辅助方法,这样我就可以在视图中使用它们,这将是非常好的,但目前我还没有找到一个干净的方法来做到这一点。

【问题讨论】:

标签: angular typescript angular5 angular-httpclient


【解决方案1】:

instanceof 检查原型链,因此除非您的 getAllMaintainableUsers() 正在创建新的 MaintainableUser 对象,否则它将返回 false。我的猜测(来自您上面的getAllUsers() 调用)是您直接从服务返回一个对象。即使您将类型注释为MaintainableUser[],由于它不是使用MaintainableUser 的原型创建的,因此data[0] instanceof MaintainableUser 的instanceof 不会返回true。

【讨论】:

    【解决方案2】:

    您的MaintainableUser type 映射得很好,因此 HttpResponse 正确映射了该泛型类型,Typescript 编译器没有抱怨,但您的响应不是 instance MaintainableUser。它仍然只是一个从 JSON 中解析出来的对象。

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

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

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

    【讨论】:

      【解决方案3】:

      理解类/对象如何工作是相当困难的。从 API 返回的只是一个普通对象(解析的 JSON),它没有任何 MaintainableUser 方法,因为它不是您的类的实例。它只具有与您的类相同的属性。

      您需要手动将值从对象传递到构造函数并创建类的实例。 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)
       );
      }
      

      【讨论】:

      • 好的。我很害怕,但希望它能做一些更花哨的事情。我来自 Java 世界,例如,如果我用 GSON 序列化 JSON,它会使用 setter 来设置值,但完成后我仍然有我的具体类。谢谢。在缺乏清洁选项的情况下,您的替代接缝足够可行。
      猜你喜欢
      • 1970-01-01
      • 2021-12-04
      • 2019-11-19
      • 2019-09-30
      • 2018-06-18
      • 1970-01-01
      • 2021-01-26
      • 1970-01-01
      • 2023-03-15
      相关资源
      最近更新 更多