首页 > 解决方案 > 如何最好地处理角度模型中的嵌套请求而不等待嵌套请求完成

问题描述

我们的 Angular 应用程序的 API 有很多由 href 引用的嵌套对象。我正在尝试将所有这些响应映射到模型,以在后端和前端之间进行一些解耦。但是,我遇到了这些嵌套 href 的问题。

这是来自服务器的示例响应:

First response:
{
    "firstName": "Steve",
    "lastName": "Mcqueen",
    "account": "api/path/account/some-id-key"
}

我的问题是:如何处理获取帐户的请求。在某些情况下可能有 3-4 个(或者更糟糕的是,位于 2-3 层深),我并不总是立即需要这些数据。最好由服务(在本例中为 AccountService)处理请求。

我考虑了几种方法。第一个也是最懒惰的是在模型中简单地将 href 存储为字符串并在需要的地方获取它。

class Client {
    firstName: string;
    lastName: string;
    account: string;
}

// in a component
this.clientService.getClient(id).subscribe(client => {
    this.accountService.getAccount(client.account).subscribe(...)
})

但是,我担心这会变得非常混乱。每次更新客户端时,四个不同的组件现在可能同时执行相同的请求。到处都有这些订阅者也不是很好看。

其次,我考虑让服务使用 tap、switchmap 进行所有获取,就像在这个答案中一样。

这里的问题是我必须等待模型中的每个请求都得到解决,然后才能开始显示任何数据。如果它像上面的例子那样很好,但情况并非总是如此。

最好的情况是,如果我能以某种方式让模型拥有 Observables 而不是 hrefs。所以客户端模型看起来像

class Client {
    firstName: string;
    lastName: string;
    account: Observable<Account>;
}

// in a component
this.clientService.getClient(id).subscribe(client => {
    client.account.subscribe(...) // no more duplicated requests
})

// In the model adapter
adapt(data) {
    return new Client(
        data.firstName,
        data.lastName,
        this.accountService.getAccount(data.account)
    )
}

但是,我正在努力解决如何最好地映射它。由于每个请求都会创建一个新模型,因此最终会导致泄漏,因为“旧”模型不会被取消订阅。除非该服务会以某种方式为相同的模型 href 重用相同的 observable 吗?

标签: angulartypescriptdesign-patternsservicemodel

解决方案


也许你需要这样的东西:

您的客户服务:

import { Injectable } from '@angular/core';
import { Observable, of, BehaviorSubject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map, first } from 'rxjs/operators';

class Client {
  firstName: string;
  lastName: string;
  account: Observable<any>;
}

@Injectable({
  providedIn: 'root',
})
export class ClientService {
  // Keep your client list in memory. only hit api one time per client.
  private clientList = {};
  constructor(private http: HttpClient) {}

  getClient(id: string): Observable<Client> {
    // if have client in memory return a observable of that client.
    if (this.clientList[id]) {
      return of(this.clientList[id]);
    }

    const url = `http://my_api/clients/${id}`;

    // hit the api to ge the client data.
    return this.http.get(url).pipe(
      map((resp: any) =>
        // builder Method only return the client Instance.
        this.createClient(id, resp.firstName, resp.lastName, resp.accountUrl),
      ),
    );
  }

  private createClient(id, firstName, lastName, accountUrl): Client {
    // BehaviorSubject its a Obvservable that keep the last value emitted in memory
    // when someone subscribe to him, he emit the last value.
    const subject = new BehaviorSubject(null);
    // subject.asObservable() this method make a Subject as Observable. To block emit method outside this clase.
    const client = { firstName, lastName, account: subject.asObservable() };

    // call to api to get the account data.
    this.http
      .get(accountUrl)
      .pipe(first()) // first operator get the first value emited and unsubscribe.
      .subscribe(accountData => subject.next(accountData)); // emiit the accountn data.

    this.clientList[id] = client;
    return client;
  }
}

在您的组件中声明一个客户端属性。

client: Client;

调用服务

this.clientService
  .getClient('21')
  .subscribe(client => (this.client = client)); 

在您的 html 中查看数据:

{{client.firstName }}
{{client.lastName }}
{{client.account | async | json }}

这样。您可以显示客户的数据,而无需等待帐户的数据。并为客户数据点击一次api,为客户帐户点击一次。如果您在许多组件之间共享服务,您将返回 clientList Attr 的客户端,而不是再次调用 api。


推荐阅读