我正在编写一个角度2的应用程序,该应用程序具有需要相互通信的同一组件的多个实例。他们共享通信服务来实现这一目标。
对于这个问题,组件称为WidgetComponent,通信服务称为CommService。
我需要一个CommService上的方法,任何WidgetComponent都可以调用它,它将返回基于每个其他WidgetComponent中的信息的信息。
逻辑流程如下:
WidgetComponent调用CommService.getInfo()
CommService请求来自所有其他小部件的信息
所有WidgetComponents都将信息返回给CommService
一旦所有WidgetComponents都回复了
CommService将结果返回到原始WidgetComponent
我知道如何使用传统的观察者模式。这是相当直接的。
CommService将有一个方法onGetInfo和属性onGetInfoSubscribers,如:
var onGetInfoSubscribers = [];
onGetInfo(callback) {
onGetInfoSubscribers.push(callback);
}
然后CommService会有另一个方法getInfo,如:
getInfo() {
var data = [];
for (var callback in onGetInfoSubscribers) {
// probably add some logic to ignore the callers info
data.push(callback());
}
// processData is just an arbitrary function that combines all the info from the WidgetComponents
return processData(data);
}
然后,每个组件在创建时都会向CommService注册,如:
var info = { ... myData ... }
CommService.onGetInfo(function() { return info });
然后他们每个人都可以调用这样的方法:
var desiredInfo = CommService.getInfo();
这样可以正常工作,但是我进入了RxJS和observables,它们提供了大量的实用功能,我想更好地理解它们,我无法理解如何实现这个相当简单的模式在RxJS。
我认为我需要在CommService中公开某种getInfoObservable,然后每个WidgetComponent都需要订阅它,然后当调用getInfo方法时,CommService需要调用next()方法getInfoObservable主题,但是不知何故,当observable触发时,每个组件都需要将其信息返回给CommService,而CommService需要知道它们何时完成。
我对代码的最好看法是在CommService中:
private infoSource = new Subject();
public info$ = this.infoSource.asObservable().combineAll(info => processData(info));
getInfo() {
this.infoSource.next();
}
然后在WidgetComponent中:
private info = { ...myData... };
commService.info$.subscribe(e => return this.info);
但我知道这甚至不是很接近,根本不起作用。
非常感谢任何正确方向的提示。特别是如果他们使用角度2版本的RxJS。
答案 0 :(得分:0)
这是我提出的解决方案。这不是你想要的,但我认为它会让你接近。我没有在将响应返回到调用窗口小部件之前聚合响应,但调用窗口小部件将在它们进入时收到所有响应。如果有必要,您可以找出聚合器。
我认为使用主题是最好的方法。 Subject是一种可观察的,可以用作Observable和Observer,它使它能够进行双向通信。
我的解决方案在CommService中使用两个主题。一个处理infoRequested事件和一个进程infoReceived事件。在您的窗口小部件不是发布者和订阅者的情况下,您可以使用一个,但在这种情况下会导致无限循环。所以我去了两个。
我们来看看。这是我的CommService:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/filter';
@Injectable()
export class CommService {
private infoRequested$: Subject<string>;
private infoSent$: Subject<WidgetData>;
constructor() {
this.infoRequested$ = new Subject<string>();
this.infoSent$ = new Subject<WidgetData>();
}
//Event that is fired when requesting information FROM a widget
infoRequested(): Observable<any> {
return this.infoRequested$.asObservable();
}
//Fired from the widget that is requesting information from all widgets
requestInfoFromAll(widgetName: string): void {
this.infoRequested$.next(widgetName);
}
//Used to send information from a widget after receiving an info Requested event
sendInfo(widgetData: WidgetData): void {
this.infoSent$.next(widgetData);
}
//Event that returns results after a widget has fired off a request Info from All
receiveInfoFromAll(widgetName: string): Observable<any> {
return this.infoSent$
.asObservable()
.filter(widget => widget.sendToWidget === widgetName);
}
}
export class WidgetData {
constructor(public name: string, public status: string, public sendToWidget: string) {}
}
初始化后,我们有了infoRequested方法。小部件订阅此事件并在触发时传输数据。当某些东西触发requestInfoFromAll事件时会被触发。
接下来是一个sendInfo方法,Widget用它来传输它的数据。最后是receiveInfoFromAll,它将是通过SendInfo传输的数据的接收器。我也为此应用了一个过滤器,用于仅将数据发送到请求它的小部件。
在组件方面,我有一个创造性地命名为Widget1Component的组件,其中包含widgetStatus和widgetName输入。
这是我的widget1.component:
import { Component, Input, OnInit } from '@angular/core';
import { CommService, WidgetData } from '../comm.service';
@Component({
selector: 'app-widget1',
template: `
<div>
<button type="button" (click)="onGetInfoFromWidgets()">Get Status from all Widgets</button>
</div>
<div *ngFor="let widget of widgets">
{{ widget.name }} - {{ widget.status }}
</div>
`
})
export class Widget1Component implements OnInit {
@Input() widgetName: string;
@Input() widgetStatus: string;
widgets: Array<WidgetData> = [];
constructor(private commSvc: CommService) {}
ngOnInit() {
//Subscribe to event requesting my information
this.commSvc
.infoRequested()
.subscribe(
//Send my info when asked
requester => this.commSvc.sendInfo(new WidgetData(this.widgetName, this.widgetStatus, requester))
);
//Subscribe to event returning all widget statuses to me
this.commSvc
.receiveInfoFromAll(this.widgetName)
.subscribe(widget => this.widgets.push(widget));
}
onGetInfoFromWidgets() {
this.widgets = [];
//Trigger comm service to get statuses.
this.commSvc.requestInfoFromAll(this.widgetName);
}
}
最后,这是我的app.component.html:
<app-widget1 widgetName="w1" widgetStatus="red"></app-widget1>
<app-widget1 widgetName="w2" widgetStatus="yellow"></app-widget1>
<app-widget1 widgetName="w3" widgetStatus="green"></app-widget1>
当您运行该应用程序时,您将获得3个按钮。单击任何按钮应该会导致所有窗口小部件的状态填充在该按钮下。
这就是全部。这是一个有趣的编码练习。我希望它能让你指出正确的方向。
答案 1 :(得分:0)
得到了解决方案:)
服务代码:
// records how many components have updated the info
let observersCount$ = new BehaviorSubject(0);
// actual info which components listen to
let info$ = new BehaviorSubject({});
/**
logic which updates info$ when all components shared the info.
This is our lucky thing.
Subject stores how many observers are listening
When the count reaches the total number of observers,
we update the info$, which sends info to all the components
**/
observersCount$
.filter(count => (
count === info$.observers.length &&
count !== 0
)
.subscribe( () => {
// perform the calculations etc etc.,
info$.next( newInfo);
// reset the count to 0
observersCount$.next(0);
});
// called by the components to update the info
public updateInfo(){
// every time a component updates info, I increment
observersCount$.next(observersCount$.value+1);
// do the stuff
}
public getInfo$(){
return this.info$;
}
组件代码:
constructor(private service: Service){
service.getInfo$()
.subscribe( info => {
// do something with latest info
});
}
...
// inform service
this.service.updateInfo();