Angular订阅从API返回的数据

时间:2018-09-28 15:27:42

标签: angular ionic-framework

我正在尝试清理我的Ionic应用并将多余的功能转移到提供程序中。以下是我的大多数API函数的外观示例(为简洁起见,我删除了许多无关的内容),并且它们运行良好。

getDataFromApi() {
    ....
    Promise.all([
        ...
    ]).then((result) => {
        let headers = new Headers();
        ...
        let body = new FormData();
        ...
        this.http.post(url, body, headers)
            .map(res => res.json())
            .subscribe(data => {
                if (data == '{}') {
                    this.mydata = [];
                } else {
                    this.mydata = data;
                }
            }, error => { });
    });
}

所以我要做的就是将函数移到提供程序中并像这样更改它

getDataFromApi() {
    ....
    Promise.all([
        ...
    ]).then((result) => {
        let headers = new Headers();
        ...
        let body = new FormData();
        ...
        return this.http.post(url, body, headers));
    });
}

然后在页面的构造函数中,我这样称呼它

this.service.getDataFromApi()
    .map(res => res.json())
    .subscribe(data => {
        if (data == '{}') {
            this.mydata = [];
        } else {
            this.mydata = data;
        }
    }, error => { });

显然,这不起作用。我已经在SO上发表了两天的帖子,也无法获得其他人的示例和答案以使其正常工作。我尝试了在提供程序中进行映射并在页面中调用它时进行了订阅,但是无论我尝试什么,都将继续收到错误。我知道此函数正在返回数据,因为在将其移到提供程序之前可以看到它。

我在做什么错了?

2 个答案:

答案 0 :(得分:2)

我认为最重要的是保持一致;您可以使用 only 承诺或使用 only可观察的对象(而不是将两者混用)来完成所有操作。

请查看 this stackblitz demo ,在这里您可以看到如何仅使用可观察对象发出相似的HTTP请求,而仅使用promise发出完全相同的请求。


使用可观察物

您可以将switchMap(或任何其他平化运算符)与fromPromise一起使用,以提供者的相同方法处理所有这些事情:

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

// RxJS 
// Please notice that this demo uses the 5.5.2 version
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators/map';
import { tap } from 'rxjs/operators/tap';
import { switchMap } from 'rxjs/operators/switchMap';

// ...

// Get a list of 5 users using observables
public getDataUsingObservables(): Observable<any> {
  const promises = Promise.all([
    this.somePromise('getDataUsingObservables', 1),
    this.somePromise('getDataUsingObservables', 2)
  ]);

  return fromPromise(promises)
    .pipe(
      switchMap(results => {
        console.log(`[getDataUsingObservables]: Both promises are finished`);
        const url = `https://randomuser.me/api/?results=5`;

        return this.http.get<any>(url);
      }),
      tap(res => {
        console.log(`[getDataUsingObservables]: The http request is finished`);
      })
  );
}

// Return a promise with the number sent as parameter 
private somePromise(callerMethod: string, aNumber: number): Promise<number> {
  console.log(`[${callerMethod}]: About to create a promise with the number ${aNumber}`);
  return Promise.resolve(aNumber);
}

然后您将像这样使用它:

this.dataService.getDataUsingObservables().subscribe(
  response => {
    this.responseUsingObservables = response;
  }, 
  error => {
    // Handle the error...
    alert(error);
  });

使用诺言

如果要使用Promise,可以使用toPromise()运算符将可观察对象转换为Promise:

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

// RxJS 
// Please notice that this demo uses the 5.5.2 version
import { tap } from 'rxjs/operators/tap';

// ...

// Get a list of 5 users using promises
public getDataUsingPromises(): Promise<any> {
  const promises = Promise.all([
      this.somePromise('getDataUsingPromises', 1),
      this.somePromise('getDataUsingPromises', 2)
  ]);

  return promises
    .then(results => {
      console.log(`[getDataUsingPromises]: Both promises are finished`);
      const url = `https://randomuser.me/api/?results=5`;

      return this.http.get<any>(url)
        .pipe(
          tap(res => {
            console.log(`[getDataUsingPromises]: The http request is finished`);
          })
        )
        .toPromise();
    });
}

// Return a promise with the number sent as parameter 
private somePromise(callerMethod: string, aNumber: number): Promise<number> {
  console.log(`[${callerMethod}]: About to create a promise with the number ${aNumber}`);
  return Promise.resolve(aNumber);
}

然后您将像这样使用它:

this.dataService.getDataUsingPromises()
  .then(response => {
    this.responseUsingPromises = response;
  })
  .catch(error => {
    // Handle the error...
    alert(error);
  });

答案 1 :(得分:0)

Disclamer :也许/应该有一种方法可以在可观测对象中实现完全相同的概念,但是我不太熟悉它们,所以也许有人可以详细说明。

您的函数应该返回一个Promise,这意味着您会这样做

getDataFromApi() {
    ....
    return Promise.all([
        ...
    ]);
}

然后在调用者函数的then部分中实现它的逻辑。现在,这没有什么好玩的,因为您将复制要消除的代码。

为此,您的函数仍应返回一个promise,它会在所需的return语句之后像这样进行解析:

getDataFromApi() {
  ....
  new Promise((resolve, reject) => {
    Promise.all([
      ...
    ]).then(result => {
      const headers = new Headers();
      ...
      const body = new FormData();
      ...
      // This is equavilent to your desired return
      resolve(this.http.post(url, body, headers));
    });
  });
}

然后在调用方then块中实现POST订阅,如下所示:

this.service.getDataFromApi()
  .then(observ => {
    observ.subscribe(result => {
    // Use your response here
    });
  });

您甚至可以使用异步函数,这在代码中会容易得多,只需将await封装在Promise.all上,然后再将{{ 1}}将会被翻译成对您的承诺解决方案。

编辑:更好的是,如果每次您只订阅JSON数据并希望返回时,该概念都可以推广使用,以使调用方中的代码更加简洁:

return

这可以通过在订阅块中的providers方法中解决promise来实现,如下所示:

this.service.getDataFromApi().then(finalData => {
  // This is the final returned data ...
}, (err) => {
//allows error to be handled
});