RXJS Observable 数组上的简单过滤器
Posted
技术标签:
【中文标题】RXJS Observable 数组上的简单过滤器【英文标题】:Simple filter on array of RXJS Observable 【发布时间】:2016-10-25 19:02:24 【问题描述】:我正在使用 Angular2 开始我的项目,开发人员似乎推荐 RXJS Observable 而不是 Promises。
我已经实现了从服务器检索元素列表(史诗)。 但是如何使用例如 id 来过滤元素?
以下代码是从我的应用程序中提取的,现在显示了最终的工作解决方案。让我们希望它对某人有所帮助。
@Injectable()
export class EpicService
private url = CONFIG.SERVER + '/app/'; // URL to web API
constructor(private http:Http)
private extractData(res:Response)
let body = res.json();
return body;
getEpics():Observable<Epic[]>
return this.http.get(this.url + "getEpics")
.map(this.extractData)
.catch(this.handleError);
getEpic(id:string): Observable<Epic>
return this.getEpics()
.map(epics => epics.filter(epic => epic.id === id)[0]);
export class EpicComponent
errorMessage:string;
epics:Epic[];
epic:Epic;
constructor(
private requirementService:EpicService)
getEpics()
this.requirementService.getEpics()
.subscribe(
epics => this.epics = epics,
error => this.errorMessage = <any>error);
// actually this should be eventually in another component
getEpic(id:string)
this.requirementService.getEpic(id)
.subscribe(
epic => this.epic = epic,
error => this.errorMessage = <any>error);
export class Epic
id: string;
name: string;
提前感谢您的帮助。
【问题讨论】:
你应该保留原来的问题,这样我们就永远不知道“不”做什么 【参考方案1】:带有修复的原始答案:
Observables
很懒惰。你必须打电话给subscribe
告诉observable
发送它的请求。
getEpic(id:number)
return this.getEpics()
.filter(epic => epic.id === id)
.subscribe(x=>...);
更新到 Rxjs 6:
import filter from 'rxjs/operators';
getEpic(id:number)
return this.getEpics()
.pipe(filter(epic => epic.id === id))
.subscribe(x=>...);
【讨论】:
subscribe的返回类型是一个Subscription,它实现了ISubscription。两者都没有过滤方法。 @StefanRein,在原始问题this.getEpics()
返回一个 Array 的 observable。所以,filter
是 Array 的方法
你是对的。就在您的回答中,您将过滤器链接在订阅方法之后,该方法没有过滤器方法,因此我的评论【参考方案2】:
您需要过滤实际的数组,而不是围绕它的 observable。
因此,您会将 Observable 的内容(即 Epic[]
)映射到过滤后的 Epic
。
getEpic(id: string): Observable<Epic>
return this.getEpics()
.map(epics => epics.filter(epic => epic.id === id)[0]);
然后你就可以subscribe
到getEpic
做任何你想做的事情。
【讨论】:
看起来很简单,但是:错误:(33, 42) TS2365:运算符“===”不能应用于“字符串”和“数字”类型。我将更新问题,以显示整个组件和服务关系。 好吧,如果是这样,那么您可以从字符串转换为数字或从数字转换为字符串:)【参考方案3】:您必须订阅 Observable
s 才能获取数据,因为 http 调用在 javascript 中是异步的。
getEpic(id: number, callback: (epic: Epic) => void)
this.getEpics().subscribe(
epics: Array<Epic> =>
let epic: Epic = epics.filter(epic => epic.id === id)[0];
callback(epic);
);
您可以像这样调用该方法:
this.someService.getEpic(epicId, (epic: Epic) =>
// do something with it
);
【讨论】:
【参考方案4】:您可以使用Observable
的flatMap
和filter
方法而不是map
中的JS 数组过滤方法来做到这一点。比如:
this.getEpics()
.flatMap((data) => data.epics) // [id: 1, id: 4, id: 3, ..., id: N]
.filter((epic) => epic.id === id) // checks id: 1, then id: 2, etc
.subscribe((result) => ...); // do something epic!!!
flatMap
将为过滤提供奇异索引,然后您可以继续处理接下来发生的任何结果。
如果 TypeScript 抛出错误,表明您无法比较字符串和数字,无论您在过滤器中使用 ==
,只需在过滤器中的 epic.id
之前添加一个 +
,根据 Angular 文档:
.flatMap(...)
.filter((epic) => +epic.id === id) // checks id: 1, then id: 2, etc
.subscribe(...)
示例:
https://stackblitz.com/edit/angular-9ehje5?file=src%2Fapp%2Fapp.component.ts
【讨论】:
这比公认的答案干净得多,并且充分利用了 Rx。正如作者指出的那样,执行此操作的正确方法是使用flatMap
。
如果史诗是一个数组(如你所建议的那样),则不能将平面映射到data.epics
。而且你也不能像你写的那样过滤数组。
@StefanRein 我认为您应该先试用代码然后发表评论 - stackblitz.com/edit/…
@mtpultz 是的,我确实从 SO 复制了您的评论作为输入。您对 SO 上的 getEpics() 方法的评论应该是:// epics: [id: 1, id: 2, id: 3, id: 4, id: 5]
而不是像 Stackblitz 示例中的 // [id: 1, id: 4, id: 3, ..., id: N]
......这让我很困惑,谢谢。也应该查看 Stackblitz 示例:)
我已将评论下移了一行以上是关于RXJS Observable 数组上的简单过滤器的主要内容,如果未能解决你的问题,请参考以下文章
Rxjs 过滤器运算符不适用于 Angular2 Observable
RxJS 将 observable 附加到 typescript 中的 observable 数组列表