从Angular 2服务创build和返回Observable
这更像是一个“最佳实践”的问题。 有三个参与者:一个Component
,一个Service
和一个Model
。 Component
正在调用Service
以从数据库中获取数据。 该Service
正在使用:
this.people = http.get('api/people.json').map(res => res.json());
返回一个Observable
。
Component
可以只订阅Observable
:
peopleService.people .subscribe(people => this.people = people); }
但是,我真正想要的是Service
返回从数据库中检索的数据创buildArray of Model
对象Array of Model
。 我意识到Component
可以在订阅方法中创build这个数组,但我认为如果服务做到这一点,并使其可用于Component
,将是更清洁。
如何可以创build一个新的Observable
,包含该数组,并返回?
更新:9/24/16angular2.0稳定
这个问题仍然有很多stream量,所以我想更新它。 随着阿尔法,Beta和7个RC候选人的疯狂变化,我停止更新我的答案,直到他们稳定。
这是使用Subject和ReplaySubject的最佳例子
我个人更喜欢使用ReplaySubject(1)
因为它允许最后存储的值在新用户附加时传递,即使是迟到:
let project = new ReplaySubject(1); //subscribe project.subscribe(result => console.log('Subscription Streaming:', result)); http.get('path/to/whatever/projects/1234').subscribe(result => { //push onto subject project.next(result)); //add delayed subscription AFTER loaded setTimeout(()=> project.subscribe(result => console.log('Delayed Stream:', result)), 3000); }); //Output //Subscription Streaming: 1234 //*After load and delay* //Delayed Stream: 1234
所以,即使我迟到或需要加载后,我总是可以得到最新的电话,而不必担心错过callback。
这也可以让你使用相同的stream来推送到:
project.next(5678); //output //Subscription Streaming: 5678
但是,如果你100%确定,你只需要打一次电话呢? 留下开放的主题和可观察性并不好,但总是有“如果?
这就是AsyncSubject进来的地方
let project = new AsyncSubject(); //subscribe project.subscribe(result => console.log('Subscription Streaming:', result), err => console.log(err), () => console.log('Completed')); http.get('path/to/whatever/projects/1234').subscribe(result => { //push onto subject and complete project.next(result)); project.complete(); //add a subscription even though completed setTimeout(() => project.subscribe(project => console.log('Delayed Sub:', project)), 2000); }); //Output //Subscription Streaming: 1234 //Completed //*After delay and completed* //Delayed Sub: 1234
真棒! 即使我们closures了这个主题,它仍然回答了最后一件事情。
另一件事是我们如何订阅该http调用并处理响应。 地图是伟大的处理响应
public call = http.get(whatever).map(res => res.json())
但是如果我们需要嵌套这些电话呢? 是的,您可以使用具有特殊function的主题:
getThing() { resultSubject = new ReplaySubject(1); http.get('path').subscribe(result1 => { http.get('other/path/' + result1).get.subscribe(response2 => { http.get('another/' + response2).subscribe(res3 => resultSubject.next(res3)) }) }) return resultSubject; } var myThing = getThing();
但这很多,意味着你需要一个function来做到这一点。 inputFlatMap
var myThing = http.get('path').flatMap(result1 => http.get('other/' + result1).flatMap(response2 => http.get('another/' + response2)));
甜, var
是一个可以从最终的http调用获取数据的可观察值。
那好,但我想要一个angular2服务!
我接到你了:
import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { ReplaySubject } from 'rxjs'; @Injectable() export class ProjectService { public activeProject:ReplaySubject<any> = new ReplaySubject(1); constructor(private http: Http) {} //load the project public load(projectId) { console.log('Loading Project:' + projectId, Date.now()); this.http.get('/projects/' + projectId).subscribe(res => this.activeProject.next(res)); return this.activeProject; } } //component @Component({ selector: 'nav', template: `<div>{{project?.name}}<a (click)="load('1234')">Load 1234</a></div>` }) export class navComponent implements OnInit { public project:any; constructor(private projectService:ProjectService) {} ngOnInit() { this.projectService.activeProject.subscribe(active => this.project = active); } public load(projectId:string) { this.projectService.load(projectId); } }
我是观察者和观察者的忠实粉丝,所以我希望这个更新能够帮助你!
原始答复
我认为这是一个使用Observable主题的用例,或者在Angular2
使用EventEmitter
。
在你的服务中,你创build一个EventEmitter
,允许你将值推送到它。 在Alpha 45中,你必须把它转换成toRx()
,但是我知道他们正在努力摆脱这个,所以在Alpha 46中,你可以简单地返回EvenEmitter
。
class EventService { _emitter: EventEmitter = new EventEmitter(); rxEmitter: any; constructor() { this.rxEmitter = this._emitter.toRx(); } doSomething(data){ this.rxEmitter.next(data); } }
这种方式有一个单一的EventEmitter
,你现在可以把不同的服务function推到上面。
如果你想直接从一个调用中返回一个observable,你可以这样做:
myHttpCall(path) { return Observable.create(observer => { http.get(path).map(res => res.json()).subscribe((result) => { //do something with result. var newResultArray = mySpecialArrayFunction(result); observer.next(newResultArray); //call complete if you want to close this stream (like a promise) observer.complete(); }); }); }
这将允许您在组件中执行此操作: peopleService.myHttpCall('path').subscribe(people => this.people = people);
并在您的服务中的电话结果搞乱。
我喜欢创buildEventEmitter
stream自己的情况下,我需要从其他组件访问它,但我可以看到两种方式工作…
这是一个显示与事件发射器的基本服务: plunkr
这是Angular2文档中关于如何创build和使用自己的Observable的一个例子:
服务
import {Injectable} from 'angular2/core' import {Subject} from 'rxjs/Subject'; @Injectable() export class MissionService { private _missionAnnouncedSource = new Subject<string>(); missionAnnounced$ = this._missionAnnouncedSource.asObservable(); announceMission(mission: string) { this._missionAnnouncedSource.next(mission) } }
组件
import {Component} from 'angular2/core'; import {MissionService} from './mission.service'; export class MissionControlComponent { mission: string; constructor(private missionService: MissionService) { missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; }) } announce() { this.missionService.announceMission('some mission name'); } }
完整的工作示例可以在这里find: https : //angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
我想补充说的是,如果创build的对象是静态的,而不是通过http来完成,那么可以这样做:
public fetchModel(uuid: string = undefined): Observable<string> { if(!uuid) { //static data return Observable.of(new TestModel()).map(o => JSON.stringify(o)); } else { return this.http.get("http://localhost:8080/myapp/api/model/" + uuid) .map(res => res.text()); } }
从答案我关于观察员和静态数据的问题: https : //stackoverflow.com/a/35219772/986160
我对派对有点迟,但是我觉得我的方法有缺点,就是没有使用EventEmitters和Subjects。
所以,这是我的方法。 我们不能脱离订阅(),我们不想。 在这种情况下,我们的服务将返回一个Observable<T>
与一个观察员,我们珍贵的货物。 从调用者,我们将初始化一个variables, Observable<T>
,它将得到服务的Observable<T>
。 接下来,我们将订阅这个对象。 最后,你得到你的“T”! 从你的服务。
首先,我们的人员服务,但是你的不通过参数,这是更现实的:
people(hairColor: string): Observable<People> { this.url = "api/" + hairColor + "/people.json"; return Observable.create(observer => { http.get(this.url) .map(res => res.json()) .subscribe((data) => { this._people = data observer.next(this._people); observer.complete(); }); }); }
好的,正如你所看到的,我们正在返回一个types为“people”的Observable
。 方法的签名,甚至这样说! 我们把_people
塞进我们的观察者。 接下来,我们将从组件中的调用者访问这个types。
在组件中:
private _peopleObservable: Observable<people>; constructor(private peopleService: PeopleService){} getPeople(hairColor:string) { this._peopleObservable = this.peopleService.people(hairColor); this._peopleObservable.subscribe((data) => { this.people = data; }); }
我们通过从PeopleService
返回Observable<people>
来PeopleService
。 然后,我们订阅这个属性。 最后,我们this.people
people
设置为我们的数据( people
)响应。
以这种方式构build服务与典型的服务有一个主要的优势:map(…)和component:“subscribe(…)”模式。 在现实世界中,我们需要将json映射到我们的类中的属性,有时我们在那里做一些自定义的东西。 所以这个映射可以发生在我们的服务中。 而且,通常,因为我们的服务调用不会被使用一次,而且可能在我们的代码的其他地方使用,所以我们不必在某个组件中再次执行该映射。 而且,如果我们给人们添加一个新的领域呢?
请注意,您正在使用Observable#map将base Observable发出的原始Response
对象转换为JSON响应的parsing表示forms。
如果我正确地理解了你,你想再次map
。 但这一次,将原始JSON转换为您的Model
实例。 所以你会做这样的事情:
http.get('api/people.json') .map(res => res.json()) .map(peopleData => peopleData.map(personData => new Person(personData)))
所以,你从一个发出Response
对象的Observable开始,把它转换成一个observable,它发出了这个响应的parsingJSON对象,然后把它转换成另一个observable,将原始的JSON转换成你的模型数组。
在service.ts文件中 –
一个。 从…的可观察到的
湾 创build一个json列表
C。 使用Observable.of()返回json对象
防爆。 –
import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; @Injectable() export class ClientListService { private clientList; constructor() { this.clientList = [ {name: 'abc', address: 'Railpar'}, {name: 'def', address: 'Railpar 2'}, {name: 'ghi', address: 'Panagarh'}, {name: 'jkl', address: 'Panagarh 2'}, ]; } getClientList () { return Observable.of(this.clientList); } };
在我们调用服务的get函数的组件中 –
this.clientListService.getClientList().subscribe(res => this.clientList = res);