如何在 angular 2 beta 的服务中有效地使用 Http 组件?

IT技术 javascript angular
2021-02-06 21:54:32

我正在尝试使用Angular 2-beta并且我想使用Http组件。但是这里有一个严重的问题:

我读了这篇文章,我知道在 Angular 2(与 Angular 1 不同)中,Http组件不是返回Promise的服务它返回一个叫做Observable 的东西我们知道一个Component最好不要直接使用Http有效的方法是制作一个负责消费Http 的服务但是怎么样?!这应该在完成请求后返回一个Promise吗?(看这里

到底有没有道理?!

2个回答

Angular 2 可以实现服务。它们仅对应于如下所述的可注入类。在这种情况下,这个类可以被注入到其他元素中,比如组件。

import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import 'rxjs/add/operator/map';

@Injectable()
export class CompanyService {
  constructor(http:Http) {
    this.http = http;
  }
}

您可以在引导应用程序的主要组件时Http指定的条件下在其中注入一个对象(使用其构造函数)HTTP_PROVIDERS

import {bootstrap} from 'angular2/platform/browser'
import {HTTP_PROVIDERS} from 'angular2/http';
import {AppComponent} from './app.component'

bootstrap(AppComponent, [
  HTTP_PROVIDERS
]);

然后可以将该服务注入到组件中,如下所述。不要忘记providers在组件列表中指定它

import { Component, View, Inject } from 'angular2/core';
import { CompanyService } from './company-service';

@Component({
  selector: 'company-list',
  providers: [ CompanyService ],
  template: `
    (...)  `
})

export class CompanyList {
  constructor(private service: CompanyService) {
    this.service = service;
  }
}

然后,您可以利用Http服务中对象实现一个方法,并返回与您的请求对应的 Observable 对象:

@Injectable()
export class CompanyService {
  constructor(http:Http) {
    this.http = http;
  }

  getCompanies() {
    return this.http.get('https://angular2.apispark.net/v1/companies/')
                  .map(res => res.json());
  }
}

然后组件可以调用此getCompanies方法并订阅 Observable 对象上的回调,以便在响应出现时通知更新组件的状态(与您在 Angular1 中使用 Promise 的方式相同):

export class CompanyList implements OnInit {
  public companies: Company[];

  constructor(private service: CompanyService) {
    this.service = service;
  }

  ngOnInit() {
    this.service.getCompanies().subscribe(
      data => this.companies = data);
  }
}

编辑

正如Foxx在他的评论中建议的那样,async管道也可用于隐式订阅可观察对象。这是使用它的方法。首先更新你的组件,将 observable 对象放在你想要显示的属性中:

export class CompanyList implements OnInit {
  public companies: Company[];

  constructor(private service: CompanyService) {
    this.service = service;
  }

  ngOnInit() {
    this.companies = this.service.getCompanies();
  }
}

然后在模板中使用异步管道:

@Component({
  selector: 'company-list',
  providers: [ CompanyService ],
  template: `
    <ul>
      <li *ngFor="#company of companies | async">{{company.name}}</li>
    </ul>
  `
})
export class CompanyList implements OnInit {
  (...)
}

这篇文章分两部分也可以提供更多细节:

希望对你有帮助,蒂埃里

我可以把它作为一个单独的问题,但这只是你答案的一个小插件。如何对 http 请求使用间隔?
2021-03-18 21:54:32
非常感谢@foox 的评论!我更新了我的答案来描述如何使用异步管道 ;-)
2021-03-20 21:54:32
整洁,我一直在寻找一个简单的例子来演示如何使用async管道。
2021-03-23 21:54:32
一个小问题,您HTTP_PROVIDERS在引导程序中导入但注入了ROUTER_PROVIDERS. 这是一个错字吗?
2021-03-30 21:54:32
您可能想要使用异步管道而不是手动订阅。
2021-04-11 21:54:32

不需要将Http的get()方法返回的observable转换成promise。在大多数情况下,服务可以简单地返回 observable。

如果我们从服务器获取数组原始类型(即字符串、数字、布尔值),我们可以通过在模板中直接使用返回的 observable 和asyncPipe来简化我们的控制器逻辑这个管道会自动订阅 observable(它也可以与 promise 一起使用),并且它会返回 observable 发出的最新值。当发出新值时,管道标记要检查更改的组件,因此视图将自动使用新值更新。

如果我们从服务器获取一个对象我不知道有什么方法可以使用 asyncPipe,我们可以使用 async 管道,结合安全导航操作符,如下所示:

{{(objectData$ | async)?.name}}

但这看起来很复杂,我们必须为我们想要显示的每个对象属性重复这一点。

相反,我建议我们subscribe()在组件中使用 observable 并将包含的对象存储到组件属性中。然后我们在模板中使用安全导航操作符(?.) 或(如@Evan Plaice 在评论中提到的)NgIf。如果我们不使用安全导航操作符或 NgIf,当模板第一次尝试渲染时将抛出错误,因为对象尚未填充值。

请注意下面的服务如何始终为每个 get 方法返回一个 observable。

服务.ts

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';  // we need to import this now

@Injectable()
export class MyService {
  constructor(private _http:Http) {}
  getArrayDataObservable() {
    return this._http.get('./data/array.json')
      .map(data => data.json());
  }
  getPrimitiveDataObservable() {
    return this._http.get('./data/primitive.txt')
      .map(data => data.text());   // note .text() here
  }
  getObjectDataObservable() {
    return this._http.get('./data/object.json')
      .map(data => data.json());
  }
}

应用程序

import {Component} from 'angular2/core';
import {MyService} from './my-service.service';
import {HTTP_PROVIDERS} from 'angular2/http';

@Component({
  selector: 'my-app',
  providers: [HTTP_PROVIDERS, MyService],
  template: `
    <div>array data using '| async':
      <div *ngFor="#item of arrayData$ | async">{{item}}</div>
    </div>
    <div>primitive data using '| async': {{primitiveData$ | async}}</div>
    <div>object data using ?.: {{objectData?.name}}</div>
    <div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
})
export class AppComponent {
  constructor(private _myService:MyService) { console.clear(); }
  ngOnInit() {
    this.arrayData$     = this._myService.getArrayDataObservable();
    this.primitiveData$ = this._myService.getPrimitiveDataObservable();
    this._myService.getObjectDataObservable()
      .subscribe(data => this.objectData = data);
  }
}

注意:我将“Observable”放在服务方法名称中——例如getArrayDataObervable()——只是为了强调该方法返回一个 Observable。通常你不会在名称中加入“Observable”。

数据/array.json

[ 1,2,3 ]

数据/primitive.json

Greetings SO friends!

数据/对象.json

{ "name": "Mark" }

输出:

array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark

Plunker


使用async管道的一个缺点是没有机制来处理组件中的服务器错误。回答了另一个问题,解释了如何在组件中捕获此类错误,但subscribe()在这种情况下我们总是需要使用

@EvanPlaice,谢谢,我更新了答案以包含您的建议。
2021-03-16 21:54:32
?(elvis) 运算符的一个有用替代方法是向*ngIf将使用数据的模板部分添加条件。它提供了更粗粒度的控制级别,因此您不必担心在整个模板上散布 elvis 运算符,也不必担心模板在没有数据的情况下呈现时的外观。
2021-04-05 21:54:32