Skip to content

Conversation

@fschmenger
Copy link
Collaborator

@fschmenger fschmenger commented Mar 18, 2024

This is my take on how we could possibly fix the layer-property-reactivity issue mentioned in #365.
To keep changes minimal I decided to wrap up OL layers and OL layer collections by proxy objects. The proxy objects behave mostly transparent and forward invocations to the underlying layer / collection objects, so they can be basically used as a drop-in. Apparently, use them only for Vue components when reactivity is required.

Using LayerProxy around an OL layer:

  • Construct a proxy object: layerProxy = new LayerProxy(myLayer)
  • Proxy objects must be destroyed before they go out of scope: layerProxy.destroy()
  • Use the underlying layer to pass it on to child components and APis: layerProxy.toRaw()
    Internally, the layer proxy traps the getProperties() and get() methods to return a self managed object of key value pairs, which is synced via OL observables.

Similarly, using LayerProxyCollection around an OL Collection:

  • Construct a proxy object: collectionProxy = new LayerCollectionProxy(myLayerCollection);
  • Proxy objects must be destroyed before they go out of scope: collectionProxy.destroy()
  • Use the underlying collection to pass it on to child components and APis: collectionProxy.toRaw()
    Internally, the collection proxy traps the forEach(), item() and getArray() methods to return LayerProxy objects.

Currently the application uses only a single instance of LayerProxyCollection, which is managed inside composables\Map. The useMap function which formerly returned a list of layers has been altered to return the LayerProxyCollection. As a result instances of LayerProxy objects are also shared between components to make this as resource friendly as possible.

However constructing additional LayerProxyCollection and LayerProxy objects is feasible if it should be required by some components (although I dont see yet why this should be necessary).

@fschmenger
Copy link
Collaborator Author

fschmenger commented Mar 18, 2024

Hi guys, here is where to take a closer look:

  • Are we happy with the overall solution? I couldn`t come up with something simpler but maybe there is...
  • LayerProxy currently only manages properties, which are passed into the constructor. Other properties are not accessible. Is this a good design or should we try and manage/sync all available properties on the layer? (However those would have reactivity restrictions as described in OL layer properties are no longer reactive #365)
  • Unfortunately proxy objects must be destroyed to free event listeners and there is no JS destructor syntax either. Currently I did not implement reference counting. So the assumption is that you only destroy Proxy objects that you have created. E.g. don`t destroy LayerProxy objects which are maintained by the LayerProxyCollection.
  • This is why LayerCollectionProxy currently does not trap and handle the pop(), push(), remove(), removeAt() and setAt() methods and just accepts/ returns raw OL layers there- see OL docs. I didn`t wanna reason about ownership patterns for now.

Unfortunately I will have to focus on other projects again in the upcoming months, so take your time for review.
If we're happy with the overall solution, feel free to work on it. It should`t be too much from here on.

Cheers Felix

@fschmenger fschmenger marked this pull request as draft March 18, 2024 13:22
…rcome problems with layer- properties reactivity.
…roperties of the layer, no more need to register for specific ones.
… Minor adjustments in various components to correctly pass the raw layer into APIs and deal with initial case when layers is undefined.
@fschmenger fschmenger force-pushed the fix_layer_property_reactivity branch from 68bc127 to 75a9fc7 Compare September 4, 2025 15:03
@fschmenger
Copy link
Collaborator Author

Hi everyone, I’ve just updated this PR.

With the changes in Vue 3, the reactivity system has improved significantly, particularly with the introduction of proxies. However, the core issue remains: Vue’s reactivity doesn't track changes made to objects unless those changes go through its reactive proxy.

In our case, OpenLayers layers are managed internally by the OpenLayers API, which means we can't guarantee that all changes to those layer objects will be made via Vue’s reactive system. To work around this limitation, I’ve introduced proxy objects that wrap the layer instances. This allows us to better integrate with Vue's reactivity while preserving compatibility with OpenLayers.

The implementation should be feature complete:

  • Proxy objects have been fully migrated for Vue 3 compatibility.
  • It's no longer necessary to explicitly list properties to be tracked — the layer proxy now observes all property changes and dynamically tracks additions and removals.
  • The Map composable has been updated to use a fully reactive LayerProxyCollection, so the required changes to consuming components are minimal.

Some of the components still use the raw OL layers, e.g. Geolocator, as they don`t require any reactivity and are not obtaining their layers via Map composable. Do we see any need to change those?

For testing: To see an example, where reactivity failed previously: Open the layer list module and switch the language.

Cheers Felix

@fschmenger fschmenger marked this pull request as ready for review September 25, 2025 08:23
@fschmenger
Copy link
Collaborator Author

Here's another problem to think about: OpenLayers defines a bunch of getter functions to obtain specific properties:
getVisible(), getOpacity(), getZIndex, getMinZoom, getMaxZoom, getMinResolution , getMaxResolution, getExtent (and possibly even getSource and getMap). As you can see from the source code ol/layer/Base.js, these map directly to the layers property object.
As of now, if we use those getters in the code, e.g. like getVisible() in layerlist/LayerListItem.vue reactivity apparently wont work.

Now there are 2 ways to deal with it:

  • Either avoid using those getters in the code in favor of the native properties when reactivity is required. E.g. invoke layer.get('visible') instead of layer.getVisible().
  • Trap the getter method calls in the proxy and redirect them to the reactive properties object. This would make getters itself reactive. This is more convenient but has the potential risk of breaking something in a future OpenLayers release. It`s not extremely likely but management of those getters is after all internal to OL and therefore undocumented behavior.

@fschmenger
Copy link
Collaborator Author

I decided to go with approach 2 and map all getter functions declared in ol/layer/Base to the internal reactive properties object. On a 2nd thought doing it in various controls like LayerList etc. does not improve the situation and only has the risk of having to fix code scattered across the whole project. I hope you agree :)

One last question: Is ReactiveLayer a better name than LayerProxy?

@fschmenger fschmenger force-pushed the fix_layer_property_reactivity branch from ccaf2f3 to 75f24ef Compare October 27, 2025 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant