Modernit JS-näkymäframeworkit (React, Angular, Vue, jne.) perustuvat näkymän tilan automaattiseen renderöintiin. Muutos tilassa johtaa muutokseen sivun DOM-rakenteessa siten että vain muuttuneet osat päivitetään.
Oskari-rpc taas perustuu metodikutsun omaisiin requesteihin sekä eventien seuraamiseen. Nämä kaksi mallia on hankalaa sovittaa yhteen ja jokainen sovellus joutuu toteuttamaan liimakerroksen itse.
RPC-sovellusten kehittämistä helpottaisi suuresti, jos olisi olemassa sovituskirjasto, joka auttaisi mäppäämään halutun sovelluskohtaisen kartan tilan ja oskari-rpc-kartan tilan. Ts. "one-way data-binding" oskari-rpc:lle.
Ajatuksen ydin olisi siinä että apuolio oskari-rpc-mediator hallinnoisi RPC-channelia ja pitäisi huolen siitä että jaetun Oskari-kartan sisäinen tila vastaisi sovelluksessa haluttua tilaa. Aina kun sovelluskohtainen kartan tila on muuttunut, sovellus kutsuisi mediatorin setState()-metodia tilan määrittelevällä objektilla.
Mediatoria luodessa annetaan sille vapaavalintainen määrä "handlereita", jotka hoitavat jonkin tilan aspektin (kuten markerit, näkyvissä olevat tasot, yms.) synkronoimisen.
Handlerin tehtävänä on pitää kirjaa oskari-kartan tilasta hoidettavan aspektin näkökulmasta ja setState:a kutsuttaessa synkronoida oskari-kartan tila vastaamaan annettua tilaobjektia. Se miten tämä tehdään on sovelluskohtaista ja märitellään handleria kirjoitettaessa.
Handler voi kuunnella Oskarin eventejä ja käyttää tarvittaessa handlerin hallinnoimaa tilaa omassa eventiin reagoimisen logiikassa. Handler voi eventin perusteella esim. lähettää Reduxin actionin tai puhtaassa React-sovelluksessa kutsua props:ssa annettua funktiota tai tuottaa Angular-eventin.
Mediator-instanssi luodaan antamalla viittaus iFrameen, iFramen domain ja lista handlereita:
const mediator = new RPCMediator(iFrameElement, iFrameDomain, [
new MarkerHandler(store),
new VectorFeatureHandler(store),
new MapViewHandler(store)
]);
Handler on mikä tahansa olio, jolla on metodit "init" ja "synchronize", esimerkistä yllä:
class MarkerHandler {
constructor(store) {
this.store = store; // Redux store
this.markers = [];
}
init(channel) {
channel.handleEvent('MapClickedEvent', (data) => {
this.store.dispatch(/* add new marker to state tree */);
});
}
synchronize(channel, state) { // state is argument of mediator.setState(...)
// add & update
state.markers.forEach((marker => {
channel.postRequest('MapModulePlugin.AddMarkerRequest', [marker.payload, marker.id]);
}));
// delete
const stateMarkers = new Map(state.markers.map((marker) => [marker.id, marker]));
const toDelete = this.markers.filter((marker) => !stateMarkers.has(marker.id));
toDelete.forEach((marker) => {
channel.postRequest('MapModulePlugin.RemoveMarkersRequest', [marker.id]);
});
this.markers = state.markers;
}
}
init() metodia kutsutaan kun kanava on valmis