处理将API对象映射到UI模型对象的模式

时间:2017-12-04 11:33:57

标签: angular typescript rxjs angular-http

我正在切换到角度使用新的HttpClient。

API am调用以一种格式返回json数据。我想获取这些数据并将其转换为适合UI使用的打字稿模型类。

之前我这样做的方法是使用map函数,例如

        return this.httpClient.get(url, { headers: headers })
        .map(mapFunction)
        .catch(errorFunction);

在map函数完成转换api响应的繁重工作时,可以引入模型对象,例如:

 const mapFunction =
            (response: Response) => {
                const data = response.json();
                const contacts: Contact[] = [];
                let i = 0;
                for (const result of data.resourceResults) {
                    i = i + 1;
                    const contact: Contact = new Contact();
                    contact.role = result.r

对我而言,这看起来非常麻烦,我基本上正在寻找一种方法将对象从api响应类型映射到ui模型类型,而不必为每个请求使用自定义映射函数。

3 个答案:

答案 0 :(得分:2)

如果没有明确指定需要映射的内容,就无法进行自定义映射。您可以告诉服务器端返回UI友好响应,或者您需要自己在客户端进行映射。

如果要在客户端映射响应,可以利用Typescript类,并使用其constructor快速生成所需的项目:

export class Contact {
    public name:string;
    public roles:any;
    constructor(name:string, roles: any) {
        //specify your own constructor logic
        this.name=name;
        this.roles=roles
    }
}

现在您可以在mapFunction中写一下,将响应显式转换为Contact列表。此外,您可以使用数组.map()来迭代对象,而无需编写for循环:

public mapFunction = (response: Response) => {
    const data = response.json();
    const resource = data.resourceResults;
    //map it
    return resource.map(result => new Contact(result.n, result.r))
}
不管是否繁琐,我认为这是主观的。但绝对可以用更优雅的方式编写代码。

答案 1 :(得分:0)

我目前对角度完全不熟悉,但由于打字稿,这仍然适用。抱歉,我的伪角度代码。最简单的方法之一是将对象映射到接口。因此,如果我有JSON响应:

{
  Id: 1,
  Name: "Bob",
  Age: 21
}

由于 TypeScript ,我可以创建一个匹配的界面:

export interface IPerson
{
  Id: number,
  Name: string,
  Age: number
}

我可以将值作为界面传递,而不必将映射值传递给类。

return (this.httpClient.get(url, { headers: headers })) as IPerson;

当然,如果你必须扩展JSON,这不是正确的解决方案。

答案 2 :(得分:0)

我要根据@CozyAzure的响应添加更多示例。

带有接口的Typescript类(为了更好地通过应用读取):

interface MessageConfig {
  MessageId?: Guid;
  SentAt?: Date;
  Status?: string;
  Text?: string;
}

export class Message {
  MessageId: Guid;
  SentAt: Date;
  Status: string;
  Text: string;

  constructor(config: MessageConfig) {
    this.MessageId = config.MessageId;
    this.SentAt = config.SentAt || new Date();
    this.Status = config.Status || "Unread";
    this.Text = config.Text;
  }
}

一个实用程序函数,用于将数据映射到Message类:

export function mapMessage(message: any) {
  return new Message({
    MessageId: message.messageId,
    SentAt: new Date(message.sentAt),
    Status: message.status,
    Text: message.text,
  });
}

将服务器响应映射到单个消息的服务功能:

addMessage = (message: Message) => {
    return this.http.post(API, message).pipe(
      map((response: any) => {
        if (response && response.success) {
          const m = mapMessage(response.message);
          return m;
        }
        throw new Error(response.errorMessage);
      }),
      catchError(error => {
        return throwError(error);
      }),
      share()
    );
  };

服务功能,地图服务器对多个消息的响应:

getMessages = (): Observable<Array<Message>> => {
    return this.http
      .get(API)
      .pipe(
        map((response: any) => {
          if (response && response.success) {
            let messages: Array<Messages> = [];
            if (response.count > 0) {
              messages = response.messages.map(
                message => mapMessage(message)
              );
            }
            return messages;
          }
          throw new Error(response.errorMessage);
        }),
        catchError(error => {
          return throwError(error);
        }),
        share()
      );
  };

所以,是的,有很多事情要做,但这就是我对这个想法进行了许多次迭代之后得出的结论。它是最容易复制和维护的,而且清晰易懂,而且相当干燥。我通常有多个API调用映射到同一类,因此5分钟的设置为每个其他API调用节省了很多时间。