将动态数组的动态数组转换为 Observable
Convert Dynamic Array of Dynamic Arrays to an Observable
我有一个动态数组结构。具体来说,它是 Google 地图的 MVCArray。这个结构有常规的 put、get、remove 方法,还有一个 addListener
来监听任何变化。库方法 (Polygon#getPaths) returns LatLngs 的 MVCArray 的 MVCArray,因为多边形可以有任意数量的路径,并且每个路径可以有任意数量的顶点。
我的目标是转换生成一个 PolygonPathEvent
的 Observable,当父 MVCArray 或任何子 MVCArray 将被更改时,它将触发。
创建 Observables
首要任务是将 addListener
转换为 Observable。
private createMVCEventObservable<T>(array: MVCArray<T>): Observable<[T[], string, number, T?]>{
const eventNames = ['insert_at', 'remove_at', 'set_at'];
return fromEventPattern(
(handler: Function) => eventNames.map(evName => array.addListener(evName,
(index: number, previous?: LatLng) => this._zone.run(() => handler.apply(array, [[array.getArray(), evName, index, previous]])))),
(handler: Function, evListeners: MapsEventListener[]) => evListeners.forEach(evListener => evListener.remove()));
}
合并 Observables
现在合并,貌似需要用到combineLatest
const pathsChanges$ = this.createMVCEventObservable(paths);
const pathChanges$ = combineLatest(paths.getArray().map(this.createMVCEventObservable));
return combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths);
);
问题是 MVCArray 的父 MVCArray 可以改变,但是 combineLatest
接受一个静态数组。所以当有新路径添加的时候,不知道如何让返回的Observable也监听这条新路径。同样,如果路径被删除,我不知道如何使返回的可观察对象取消订阅已删除的路径。
管道到主题(错误的方法)
我考虑过返回一个 Subject,并在父 MVCArray<MVCArray<LatLng>>
更改时简单地将它订阅到不同的 observables。
const retVal: Subject<PolygonPathEvent> = new Subject();
const pathsChanges$ = this.createMVCEventObservable(paths);
const pathChanges$ = combineLatest(paths.getArray().map(this.createMVCEventObservable));
let latestSubscription = combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths)
).subscribe(retVal);
pathsChanges$.pipe(tap( ([arrays, event, index, previous]) => {
latestSubscription.unsubscribe();
latestSubscription = combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths)
).subscribe(retVal);
} ));
return retVal;
这行得通,问题是对原始 Observable(和 addListener)的订阅就在这个方法中发生,而不是在订阅返回的 Observable 时发生。
结论
为此我需要某种运算符。
如果我对你的问题理解正确,可能会有space使用switchMap
解决。
你说“问题是 MVCArray 的父 MVCArray 可以改变,但是 combineLatest 接受一个静态数组”。我认为您引用的静态数组是由 pathChanges$
Observable 发出的,它在代码片段的开头一次性创建,并且在 MVCArray 的父 MVCArray 更改时不会更新。
如果我的理解是正确的,我们需要做的是提供一种方法来通知MVCArray 的父MVCArray 发生变化的事件,并在任何时候发生此类事件时,再次执行combineLatest
函数使用新的更新数组。
这可以通过类似于以下的逻辑来完成
const pathsChanges$ = this.createMVCEventObservable(paths);
pathsChanges$
.pipe(
switchMap(() => combineLatest(paths.getArray().map(this.createMVCEventObservable))),
map(pathArr => new PolygonPathEvent(pathArr, paths))
)
这个逻辑假设变量paths
是从外部传入这段逻辑的。
我无法重现这个案例,因此我不确定我的回答是否能解决您的问题,即使我希望它能有所帮助。
我有一个动态数组结构。具体来说,它是 Google 地图的 MVCArray。这个结构有常规的 put、get、remove 方法,还有一个 addListener
来监听任何变化。库方法 (Polygon#getPaths) returns LatLngs 的 MVCArray 的 MVCArray,因为多边形可以有任意数量的路径,并且每个路径可以有任意数量的顶点。
我的目标是转换生成一个 PolygonPathEvent
的 Observable,当父 MVCArray 或任何子 MVCArray 将被更改时,它将触发。
创建 Observables
首要任务是将 addListener
转换为 Observable。
private createMVCEventObservable<T>(array: MVCArray<T>): Observable<[T[], string, number, T?]>{
const eventNames = ['insert_at', 'remove_at', 'set_at'];
return fromEventPattern(
(handler: Function) => eventNames.map(evName => array.addListener(evName,
(index: number, previous?: LatLng) => this._zone.run(() => handler.apply(array, [[array.getArray(), evName, index, previous]])))),
(handler: Function, evListeners: MapsEventListener[]) => evListeners.forEach(evListener => evListener.remove()));
}
合并 Observables
现在合并,貌似需要用到combineLatest
const pathsChanges$ = this.createMVCEventObservable(paths);
const pathChanges$ = combineLatest(paths.getArray().map(this.createMVCEventObservable));
return combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths);
);
问题是 MVCArray 的父 MVCArray 可以改变,但是 combineLatest
接受一个静态数组。所以当有新路径添加的时候,不知道如何让返回的Observable也监听这条新路径。同样,如果路径被删除,我不知道如何使返回的可观察对象取消订阅已删除的路径。
管道到主题(错误的方法)
我考虑过返回一个 Subject,并在父 MVCArray<MVCArray<LatLng>>
更改时简单地将它订阅到不同的 observables。
const retVal: Subject<PolygonPathEvent> = new Subject();
const pathsChanges$ = this.createMVCEventObservable(paths);
const pathChanges$ = combineLatest(paths.getArray().map(this.createMVCEventObservable));
let latestSubscription = combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths)
).subscribe(retVal);
pathsChanges$.pipe(tap( ([arrays, event, index, previous]) => {
latestSubscription.unsubscribe();
latestSubscription = combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths)
).subscribe(retVal);
} ));
return retVal;
这行得通,问题是对原始 Observable(和 addListener)的订阅就在这个方法中发生,而不是在订阅返回的 Observable 时发生。
结论
为此我需要某种运算符。
如果我对你的问题理解正确,可能会有space使用switchMap
解决。
你说“问题是 MVCArray 的父 MVCArray 可以改变,但是 combineLatest 接受一个静态数组”。我认为您引用的静态数组是由 pathChanges$
Observable 发出的,它在代码片段的开头一次性创建,并且在 MVCArray 的父 MVCArray 更改时不会更新。
如果我的理解是正确的,我们需要做的是提供一种方法来通知MVCArray 的父MVCArray 发生变化的事件,并在任何时候发生此类事件时,再次执行combineLatest
函数使用新的更新数组。
这可以通过类似于以下的逻辑来完成
const pathsChanges$ = this.createMVCEventObservable(paths);
pathsChanges$
.pipe(
switchMap(() => combineLatest(paths.getArray().map(this.createMVCEventObservable))),
map(pathArr => new PolygonPathEvent(pathArr, paths))
)
这个逻辑假设变量paths
是从外部传入这段逻辑的。
我无法重现这个案例,因此我不确定我的回答是否能解决您的问题,即使我希望它能有所帮助。