I have a dynamic array structure. Specifically, it is Google Maps' MVCArray. This structure has regular put, get, remove methods, as well as an addListener
to listen to any changes. A library method (Polygon#getPaths) returns an MVCArray of MVCArray of LatLngs, since a polygon can have any number of paths, and each path can have any number of vertices.
My goal is to convert generate an Observable of PolygonPathEvent
which will fire when either the parent MVCArray any child MVCArray will be changed.
First order of business is to convert the addListener
to Observables.
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()));
}
Now to combine, seemingly we need to use combineLatest
const pathsChanges$ = this.createMVCEventObservable(paths);
const pathChanges$ = combineLatest(paths.getArray().map(this.createMVCEventObservable));
return combineLatest(pathChanges$, pathsChanges$, (pathArr, paths) =>
new PolygonPathEvent(pathArr, paths);
);
The problem is that the parent MVCArray of MVCArray can change, but combineLatest
takes in a static array. So when there is a new path added, I don't know how to make the returned Observable also listen to this new path. Same, if a path is deleted, I don't know how to make the returned observable unsubscribe from the deleted path.
I thought about returning a Subject, and simply subscribing it to different observables whenever the parent MVCArray<MVCArray<LatLng>>
changes.
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;
This works, the problem is that a subscription to the original Observables (and addListener) happens right in this method, and not when the returned Observable is subscribed to.
I need some kind of operator for this.
If I understand your problem right, there may be space to use switchMap
to solve it.
You say that "The problem is that the parent MVCArray of MVCArray can change, but combineLatest takes in a static array". I think that the static array you refer to is the one emitted by the pathChanges$
Observable, which is created once for all at the beginning of your code snippet and gets not updated when the parent MVCArray of MVCArray changes.
If my understanding is right, what we need to do is to provide a way to notify the event of change of the parent MVCArray of MVCArray and, any time such event occurs, execute again the combineLatest
function using the new updated array.
This can be accomplished with a logic similar to the following
const pathsChanges$ = this.createMVCEventObservable(paths);
pathsChanges$
.pipe(
switchMap(() => combineLatest(paths.getArray().map(this.createMVCEventObservable))),
map(pathArr => new PolygonPathEvent(pathArr, paths))
)
This logic assumes that the variable paths
is passed into this piece of logic from the outside.
I could not reproduce the case and therefore I am far from sure that my answer solves your problem, even if I hope it helps.