从角度2服务创建和返回可观测数据

这更像是一个“最佳实践”问题。有三个球员: 一个 Component,一个 Service和一个 ModelComponent正在调用 Service以从数据库中获取数据。Service使用:

this.people = http.get('api/people.json').map(res => res.json());

返回一个 Observable

Component可以订阅 Observable:

    peopleService.people
.subscribe(people => this.people = people);
}

但是,我真正想要的是 Service返回一个 Array of Model对象,这个对象是从 Service从数据库检索到的数据中创建的。我意识到 Component可以只在订阅方法中创建这个数组,但是我认为如果服务这样做并且让它对 Component可用,那么会更简单。

Service如何创建一个包含该数组的新 Observable并返回它?

249532 次浏览

更新: 9/24/16角度2.0稳定

这个问题仍然得到了很多流量,所以,我想更新它。由于 Alpha,Beta 和7个 RC 候选人的疯狂变化,我停止更新我的 SO 答案,直到它们稳定下来。

这是使用 实验对象重播对象的完美案例

私下里更喜欢使用 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

因此,即使我附加或需要加载后,我总是可以得到最新的呼叫,而不用担心错过回调。

这也允许您使用相同的流向下压到:

project.next(5678);
//output
//Subscription Streaming: 5678

但是,如果您100% 确定只需要执行一次呼叫,那该怎么办呢?留下开放的主题和可观察的东西是不好的,但总是有那个 “如果?”

这就是 异步主题的用武之地。

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

太棒了! 即使我们结束了这个话题,它还是回复了它载入的最后一个东西。

另一个问题是我们如何订阅 http 调用并处理响应。地图非常适合处理响应。

public call = http.get(whatever).map(res => res.json())

但是如果我们需要嵌套这些调用呢? 是的,您可以使用具有特殊功能的主题:

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();

但是这个数量很大,意味着需要一个函数来完成。输入 平面地图:

var myThing = http.get('path').flatMap(result1 =>
http.get('other/' + result1).flatMap(response2 =>
http.get('another/' + response2)));

亲爱的,var是一个可观察的,从最后的 http 调用获取数据。

好的,太好了,但是我想要一个有棱角的服务!

我抓住你了:

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);
}


}

我是观察者和观察者的超级粉丝,所以我希望这个更新能有所帮助!

原始答案

我认为这是使用 可观察科目或者在 Angular2中使用 EventEmitter的一个用例。

在您的服务中,您创建了一个 EventEmitter,该 EventEmitter允许您将值推送到它上面。在 A45中你必须用 toRx()转换它,但是我知道他们正在努力摆脱它,所以在 A46中你可以简单地返回 EvenEmitter

class EventService {
_emitter: EventEmitter = new EventEmitter();
rxEmitter: any;
constructor() {
this.rxEmitter = this._emitter.toRx();
}
doSomething(data){
this.rxEmitter.next(data);
}
}

这种方式有一个单一的 EventEmitter,您不同的服务功能现在可以推上。

如果你想直接从一个呼叫中返回一个可观察的数据,你可以这样做:

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);

然后搞乱你服务的电话的结果。

我喜欢自己创建 EventEmitter流,以防我需要从其他组件访问它,但我可以看到两种工作方式..。

下面是一个活塞,显示了一个基本的服务与事件发射器: 普朗克

我想补充的是,如果创建的对象是静态的,而不是通过 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());
}
}

编辑: 对于 Angular 7.x.x 映射,需要使用下面描述的管道()来完成(https://stackoverflow.com/a/54085359/986160) :

import {of,  Observable } from 'rxjs';
import { map } from 'rxjs/operators';
[...]
public fetchModel(uuid: string = undefined): Observable<string> {
if(!uuid) { //static data
return of(new TestModel());
}
else {
return this.http.get("http://localhost:8080/myapp/api/model/" + uuid)
.pipe(map((res:any) => res)) //already contains json
}
}

从回答我关于观测者和静态数据的问题: https://stackoverflow.com/a/35219772/986160

这是 Angular2医生中的一个例子,说明了如何创建和使用自己的可观测数据:

服务

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');
}
}

你可在此找到完整及可行的例子: Https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

我有点迟到了,但是我认为我的方法有一个优点,那就是它没有使用 EventEmitters 和 Subject。

所以,这是我的方法。我们无法摆脱订阅() ,而且我们也不想这样做。在这种情况下,我们的服务将返回一个 Observable<T>与观察员,有我们的宝贵货物。我们将从调用方初始化一个变量 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();




});
});
}

好的,如你所见,我们返回一个 Observable类型的“人”。该方法的签名,甚至这样说!我们把 _people物体塞进观测器。接下来,我们将从组件中的调用方访问此类型!

在组件中:

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>来初始化 _peopleObservable。然后,我们订阅这个属性。最后,我们将 this.people设置为数据(people)响应。

以这种方式构建服务比典型的服务有一个主要优势: map (...)和组件: “ ordering (...)”模式。在现实世界中,我们需要将 json 映射到我们的类中的属性,有时,我们在那里做一些自定义的工作。因此这个映射可以在我们的服务中发生。并且,通常,由于我们的服务调用不会被使用一次,而是可能在代码中的其他位置,因此我们不必再次在某个组件中执行该映射。此外,如果我们向人们添加一个新字段会怎样?....

请注意,您正在使用 可观测的 # 地图将基本 Observer 发出的原始 Response对象转换为 JSON 响应的解析表示。

如果我没有理解错的话,你想再来一次 map。但是这一次,将原始 JSON 转换为 Model的实例。所以你会这样做:

http.get('api/people.json')
.map(res => res.json())
.map(peopleData => peopleData.map(personData => new Person(personData)))

所以,您从一个发出 Response对象的观察对象开始,将其转换为一个发出响应的解析 JSON 对象的观察对象,然后将其转换为另一个观察对象,将原始 JSON 转换为模型的数组。

在 service.ts 文件中-

从可观察到的[的]进口
创建一个 json 列表
使用 Observer able.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);