【问题标题】:Have multiple components build one HTTP request让多个组件构建一个 HTTP 请求
【发布时间】:2017-08-04 23:04:04
【问题描述】:

目前我的应用程序有一个数据服务,它向服务器发出 http 请求以获取数据。每个组件都会单独调用此服务以获取所需的数据。

public getHospitals() {
    return this._http.get(this.baseUrl + "/hospitals/")
        .map(res => res.json())
        .map(json => automapper.map(typeof (JSON), Hospital, json));
}

public getClinics() {
    return this._http.get(this.baseUrl + "/clinics/")
        .map(res => res.json())
        .map(json => automapper.map(typeof (JSON), Clinic, json));
}

当我加载我的示例页面时,我有一个 Hospital 组件和一个 Clinic 组件。这两个组件调用数据服务中的相应方法。

我最近扩展了服务器以允许单个请求指定它需要什么数据。下面是一个如何调用它的示例。

public getLocalData(body) {
    const headers = new Headers({ "Content-Type": "application/json" });
    const options = new RequestOptions({ headers });
    return this._http.post(this.baseUrl, JSON.stringify(body), options)
        .map(res => res.json());
}

public getHospitals() {
    return this.getLocalData(["Hospitals"])
        .map(json => automapper.map(typeof (JSON), Hospital, json.Hospitals));
}

public getClinics() {
    return this.getLocalData(["Clinics"])
        .map(json => automapper.map(typeof (JSON), Clinic, json.Clinics));
}

这可以改为这样调用,这将返回所有数据:const localData = this.getLocalData(["Hospitals", "Clinics"]);

我的问题是,如何修改这两个组件来为getLocalData 方法构建body 参数并且只发出一个http 请求?

编辑:我也在寻求扩展它,以便最终我可以说 10 多个组件,每个组件都有自己的一组本地数据要请求。

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    一旦调用您的服务来获取数据,您可以等到所有 ngZone 微任务完成(包括可能对服务发出的任何其他请求),然后触发单个 http 请求。您需要修改 getLocalData 方法以返回 rxjs Subject ,一旦数据从服务器返回,您就可以发出有关主题的数据,以便将它们传递回适当的调用者。

    免责声明我不完全确定这是否是解决此问题的最佳方法,但上述方法应该可行。

    这里是修改后的服务和getLocalData方法。

    import {Observable} from "rxjs/Observable";
    import "rxjs/add/operator/map";
    import "rxjs/add/operator/first";
    import {Subject} from "rxjs/Subject";
    
    // ...
    
    public getHospitals() {
        return this.getLocalData("Hospitals")
            .map(json => automapper.map(typeof (JSON), Hospital, json.Hospitals));
    }
    
    public getClinics() {
        return this.getLocalData("Clinics")
            .map(json => automapper.map(typeof (JSON), Clinic, json.Clinics));
    }
    
    private requests = {};
    private isAggregating = false;
    private getLocalData(dataKey): Subject {
        this.requests[dataKey] = this.requests[dataKey] || new Subject();
    
        if(!this.isAggregating){
            this.isAggregating = true;
            this.ngZone.onMicrotaskEmpty.first().subscribe(() => {
                var requests = this.requests;
                this.requests = {};
                this.isAggregating = false;
    
                const headers = new Headers({ "Content-Type": "application/json" });
                const options = new RequestOptions({ headers });
    
                this._http.post(this.baseUrl, JSON.stringify(Object.keys(requests)), options)
                    .map(res => {
                        return res.json();
                    }).subscribe((data) => {
                        Object.keys(data)
                            .filter((key) => requests[key])
                            .forEach((key)=>{
                                requests[key].next(data[key]);
                                requests[key].complete();
                            });
                    });
           });
       }
    
       return this.requests[dataKey];
    

    }

    我不确定如何重写它,以便各个服务方法可以请求多组数据,如您的问题中所示,但我确信有一种方法可以使用 requests 映射创建以启用请求多个集合而不是单个集合。

    Demo

    【讨论】:

      【解决方案2】:

      可能你想实现这样的东西:

      @Injectable()
      export class LocalDataService {
          private getLocalData(body) {
              const headers = new Headers({ "Content-Type": "application/json" });
              const options = new RequestOptions({ headers });
              return this._http.post(this.baseUrl, JSON.stringify(body), options)
                  .map(res => res.json());
          }
      
          public getHospitals() {
              if (!this.pendingRequest) {
                  this.pendingRequest = this.getLocalData(["Hospitals", "Clinics"]);
                  this.cleanUpSubscription = this.pendingRequest.subscribe(() => this.cleanUp(), () => this.cleanUp());
              }
      
              return this.pendingRequest.map(json => automapper.map(typeof (JSON), Hospital, json.Hospitals));
          }
      
          public getClinics() {
              if (!this.pendingRequest) {
                  this.pendingRequest = this.getLocalData(["Hospitals", "Clinics"]);
                  this.cleanUpSubscription = this.pendingRequest.subscribe(() => this.cleanUp(), () => this.cleanUp());
              }
      
              return this.pendingRequest.map(json => automapper.map(typeof (JSON), Clinic, json.Clinics));
          }
      
          private cleanUp() {
              if (this.cleanUpSubscription) {
                  this.cleanUpSubscription.unsubscribe();
                  this.cleanUpSubscription = null;
              }
      
              this.pendingRequest = null;
          }
      
          private pendingRequest: Observable<Response> = null;
          private cleanUpSubscription: Subscription = null;
      }
      

      我已经缓存了 observable,以便在后续请求中返回。因此,如果我们有一个请求正在进行中(比如说医院),并要求 API 获取诊所,直到医院没有返回但我们不会提出额外的请求。现有的 observable 将被返回,两个组件(医院和诊所)都可以单独处理响应。

      我还添加了一些清理代码,以便在请求完成后删除缓存的 Observable。后续请求将再次访问 API。

      注意:我尚未测试此代码,因此可能需要进行一些额外的更改。

      【讨论】:

      • 这将如何扩展和扩展?一旦接到多达 20 种不同类型的数据呼叫,我会怎么做?并非每个页面都需要进行所有这些调用,因此我不想请求所有这些调用。
      • 没有什么说您的问题中有两个以上的组成部分。因此,请根据您的实际要求更新您的问题。如果您想对多个独立组件提出一个请求,我认为您应该有一个了解其子组件(诊所、医院等)的父组件。并且当所有子组件都加载完毕后(ngAfterViewInit),父组件可以进行网络请求,通过Input参数将Observable传递给子组件。
      猜你喜欢
      • 1970-01-01
      • 2011-02-18
      • 1970-01-01
      • 1970-01-01
      • 2013-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多