嵌套可观测对象和以角度加载内容的问题

问题描述

我开始使用天气api创建我的第一个应用程序,在第一个加载视图中,我希望通过订阅可观察对象来发出3个请求,这三个类型均为get,每个类型都取决于以下内容:>

1-获取所有状态的列表 2-获取每个州的部门列表 3-从每个随机状态获得部门的预测

我要面对的问题是,在发出不同的串联请求时,在所有加载完成之前,我不希望在表中加载数据,但这使我非常困难,因为我尝试放置在代码的开始处有一个加载变量,在变量的末尾有一个加载变量,该变量存在于第2点和第3点之间,但不能按预期工作。

我的代码如下:

general.component.ts:

import { Component,OnInit } from '@angular/core';
import { eltiempoDataService } from 'src/app/services/eltiempo-data.service';
import { weatherGeneralModel } from 'src/app/models/weatherGeneral.model';


@Component({
  selector: 'app-general',templateUrl: './general.component.html',styleUrls: ['./general.component.css']
})
export class GeneralComponent implements OnInit {

  data_mun: weatherGeneralModel[] = [];
  loading_data: boolean;

  constructor(private eltiempoDataService: eltiempoDataService) {
    this.getRandomMunicipio()
    console.log('finish2')
  }

  ngOnInit() {
  }

  getRandomMunicipio() {
    let provs
    let prov_random: {}
    this.getProvincias().subscribe((data: any[]) => {
      this.loading_data = true;
      provs = data['provincias']
      this.processData(provs)
    })
  }

  processData(provs: any[]){
    for (let prop in provs) {
      this.getMunicipios(provs[prop]['CODPROV']).subscribe((data_municipios) => {
        let object_data_general: weatherGeneralModel = new weatherGeneralModel()
        let local_muni: any[] = data_municipios['municipios']
        var random = Math.floor(Math.random() * local_muni.length);
        let cod_ine_mun = local_muni[random]['CODIGOINE'].toString().substring(0,5);
        this.getDataMunicipio(provs[prop]['CODPROV'],cod_ine_mun).subscribe((data_predict) => {
          object_data_general.comunidad = provs[prop]['COMUNIDAD_CIUDAD_AUTONOMA']
          object_data_general.provincia = provs[prop]['NOMBRE_PROVINCIA']
          object_data_general.municipio = data_predict['municipio']['NOMBRE']
          object_data_general.pronostico = data_predict['temperaturas']['max'] + '/' + data_predict['temperaturas']['min']
          object_data_general.cod_prov = provs[prop]['CODPROV']
          this.data_mun.push(object_data_general)
        })
      })
    }
    this.loading_data = false;
  }

  getProvincias() {
    return this.eltiempoDataService.getProvincias()
  }

  getMunicipios(cod_prov: string) {
    return this.eltiempoDataService.getMunicipios(cod_prov)
  }

  getDataMunicipio(cod_prov: string,id_muni: string) {
    return this.eltiempoDataService.getDataMunicipio(cod_prov,id_muni)
  }
}

eltiempo-dataService.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient,HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class eltiempoDataService {

  constructor(private http: HttpClient) {
    console.log('servicio ElTiempo.net listo')
  }

  getProvincias(){
    return this.http.get('https://www.el-tiempo.net/api/json/v2/provincias')
  }

  getMunicipios(cod_prov:string){
    return this.http.get(`https://www.el-tiempo.net/api/json/v2/provincias/${cod_prov}/municipios`)
  }

  getDataMunicipio(cod_prov:string,id_muni:string){
    return this.http.get(`https://www.el-tiempo.net/api/json/v2/provincias/${cod_prov}/municipios/${id_muni}`)
  }
}

general.component.html:

 
<div class="contianer mt-5 m-sm-5">
  <div class="table-responsive">
    <table class="table table-striped table-dark" *ngIf="!loading_data">
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">Comunidad</th>
          <th scope="col">Provincia</th>
          <th scope="col">Muicipio</th>
          <th scope="col">Prediccion</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let data of data_mun">
          <th scope="row">{{data.CODPROV}}</th>
          <td>{{data.comunidad}}</td>
          <td>{{data.provincia}}</td>
          <td>{{data.municipio}}</td>
          <td>{{data.pronostico}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

我认为我没有遵循最佳实践,将不胜感激。

谢谢大家

解决方法

这是StackBlitz链接: https://stackblitz.com/edit/nested-api-calls-forkjoin?file=src/app/app.component.ts

首先,我建议为所有端点创建接口,因为这是打字稿的强大功能,因为它知道您将获取哪种数据,因此您会从IDE中获得很多帮助。

我导出了一些端点接口以查看其工作方式:

export interface Municipios {
  breadcrumb: {
    name: string;
    title: string;
    url: string;
  }[];
  codprov: string;
  keywords: string;
  metadescripcion: string;
  municipios: Municipio[];
  provincia: string;
  title: string;
}

在您的服务中,它将是这样。

  getMunicipios(cod_prov: string): Observable<Municipios> {
    return this.http.get<Municipios>(
      `https://www.el-tiempo.net/api/json/v2/provincias/${cod_prov}/municipios`
    );
  }

第二个:服务,接口和类的命名约定为PascalCase 因此eltiempoDataService将是EltiempoDataService

第三:由于您需要来自第一个API请求结果的第一个数组中的数据,因此您可以使用forkJoin来简单地将所有请求一起发送,当它们准备就绪时,您可以使用答案。 forkJoin:https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin

因此,您可以在此处创建一个包含省份且可观察到的下一个数据的数组。

    this.loading_data = true;
    const provinciasGetMunicipiosObservables: {
      province: Province;
      getMunicipios: Observable<Municipios>;
    }[] = [];

    const getDataMunicipioObservables: { // This is for second wave of API Calls
      province: Province;
      municipios: Municipios;
      getDataMunicipio: Observable<DataMunicipio>;
    }[] = [];

    provincias.forEach(province => {
      /***************************
       **** Creating Array of province and  getMunicipios observable
       ***************************/
      provinciasGetMunicipiosObservables.push({
        province,getMunicipios: this.getMunicipios(province.CODPROV)
      });
    });

当数组准备好时,您可以将其传递给forkJoin,当所有数组的结果准备好时,它将给出答案。

注意:因为我没有创建观察对象的绝对数组,所以我只需要map观察对象,因此它会返回观察对象数组

observable内部的管道用于映射该特定Observable的答案,当它可以访问省时,我将其映射到Answer内部。

    forkJoin(
      provinciasGetMunicipiosObservables.map(x => {
        return x.getMunicipios.pipe(
          map(y => {
            return {
              province: x.province,getMunicipiosAnswer: y
            };
          })
        );
      })
    )
      .pipe(first())
      .subscribe(municipiosArray => {
// Time to send Second Wave of API requests

}

您只需像以前一样重复该过程即可。

      .subscribe(municipiosArray => {
        municipiosArray.forEach(municipios => {
          /***************************
           **** Part two of data when we got getMunicipios
           ***************************/
          let local_muni: any[] = municipios.getMunicipiosAnswer.municipios;
          var random = Math.floor(Math.random() * local_muni.length);
          let cod_ine_mun = local_muni[random]["CODIGOINE"]
            .toString()
            .substring(0,5);
          /***************************
           **** Creating second array for getting getDataMunicipio
           ***************************/
          getDataMunicipioObservables.push({
            municipios: municipios.getMunicipiosAnswer,province: municipios.province,getDataMunicipio: this.getDataMunicipio(
              municipios.province.CODPROV,cod_ine_mun
            )
          });
        });
          /***************************
           **** Send Requests with forkJoin
           ***************************/
        forkJoin(
          getDataMunicipioObservables.map(x => {
            return x.getDataMunicipio.pipe(
              map(y => {
                return {
                  province: x.province,municipios: x.municipios,getDataMunicipioAnswer: y
                };
              })
            );
          })
        ).subscribe(arrayOfFinalData => {
          // temproray array
          const finalData: WeatherGeneral[] = [];
          arrayOfFinalData.forEach(weatherData => {
            const max = weatherData.getDataMunicipioAnswer.temperaturas.max;
            const min = weatherData.getDataMunicipioAnswer.temperaturas.min;
            const pronostico = `${max}/${min}`;
            const weatherGeneral = new WeatherGeneral({
              cod_prov: weatherData.province.CODPROV,comunidad: weatherData.province.COMUNIDAD_CIUDAD_AUTONOMA,municipio: weatherData.getDataMunicipioAnswer.municipio.NOMBRE,pronostico,provincia: weatherData.province.NOMBRE_PROVINCIA
            });
            finalData.push(weatherGeneral);
          });
           this.loading_data = false;
          this.data_mun = [...finalData];
        });
      });

并随时询问是否不清楚。