如何在angular度2testing版的服务中有效地使用Http组件?
我正在尝试使用Angular 2-beta ,我想使用Http组件。 但是这里有一个严重的问题:
我读了这个 ,我知道Angular 2(不同于Angular 1), Http组件不是一个返回Promise的服务。 它返回一个叫Observable的东西。 我们知道一个Component最好不要直接使用Http 。 有效的方法是制作一个负责使用Http的服务 。 但是怎么样? 如果这个在完成请求后返回一个承诺? (看这里 )
这是否有意义?!
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_PROVIDERS
的条件下(使用其构造函数)在其中注入Http
对象:
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对象上订阅一个callback,当响应在那里更新组件的状态时(通过与在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在他的评论中所build议的那样, async
pipe道也可以用来隐式地订阅可观察对象。 这是使用它的方法。 首先更新组件,将可观察对象放入要显示的属性中:
export class CompanyList implements OnInit { public companies: Company[]; constructor(private service: CompanyService) { this.service = service; } ngOnInit() { this.companies = this.service.getCompanies(); } }
然后使用模板中的asynchronouspipe道:
@Component({ selector: 'company-list', providers: [ CompanyService ], template: ` <ul> <li *ngFor="#company of companies | async">{{company.name}}</li> </ul> ` }) export class CompanyList implements OnInit { (...) }
这篇文章分两部分可以提供更多的细节:
- http://restlet.com/blog/2015/12/30/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-1/
- http://restlet.com/blog/2016/01/06/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-2/
希望它能帮助你,Thierry
没有必要将Http的get()方法返回的observable转换为promise。 在大多数情况下,服务可以简单地返回observable。
如果我们从服务器获取一个数组或一个原始types (即string,数字,布尔值),我们可以通过在我们的模板中直接使用返回的observable和asyncPipe来简化我们的控制器逻辑。 这个pipe道会自动订阅observable(它也可以和promise一起工作),它会返回observable发出的最新值。 当一个新的值发出时,pipe道标记要检查的组件被更改,因此视图将自动更新新的值。
如果我们从服务器获取一个对象 , 我不知道有什么方法可以使用asyncPipe,我们可以使用asyncpipe道,结合安全导航操作符,如下所示:
{{(objectData$ | async)?.name}}
但是看起来很复杂,我们不得不重复一遍我们想要显示的每个对象属性。
相反,我build议我们subscribe()
到控制器中的observable并将包含的对象存储到组件属性中。 然后,我们使用模板中的安全导航运算符 (?)或(如@Evan Plaice在评论中提到的那样)NgIf。 如果我们不使用安全导航操作符或NgIf,模板第一次尝试渲染时将会抛出一个错误,因为该对象尚未填充值。
注意下面的服务总是返回每个get方法的observable。
service.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()); } }
app.ts
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。 通常情况下,你不会在名称中join“Observable”。
数据/ array.json
[ 1,2,3 ]
数据/ primitive.json
Greetings SO friends!
数据/ object.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
pipe道的一个缺点是没有机制来处理组件中的服务器错误。 我回答了另外一个问题 ,解释了如何在组件中捕获这样的错误,但是在这种情况下我们总是需要使用subscribe()
。