Leaflet l-marker-cluster is closing the opened popup when cluster is changed

84 Views Asked by At

The problem: When the user opens a marker popup sometimes it is necessary to zoom out so the user can see bigger portion of the map. Once the selected marker gets added to a cluster or clusters get re-rendered the popup closes and the user is not able to see which marker was opened

The question: How can I prevent this situation and leave the popup opened until the user actually closes the popup?

I have the following setup:

  • Nuxt2
  • Leaflet
  • Leaflet.markercluster

I have build the map in different components because I am using Atomic Design. Here are the separate components:

Map.vue

<l-map id="myMap" ref="mapElement" :zoom="zoom" :center="center" class="gkn-map__map" :options="{scrollWheelZoom: !isMobile, touchZoom: isMobile, dragging: !isMobile}">
  <l-tile-layer :url="url" :attribution="attribution" />
  <MoleculeMapCluster :map-items="mapItems"/>
</l-map>

MapCluster.vue

<l-marker-cluster class="cluster" @animationend="animationEnded()">
  <MoleculeMapMarker v-for="(item, index) in mapItems" :key="index" :map-items="mapItems" :item="item" />
</l-marker-cluster>

MapMarker.vue

<l-marker v-for="(coord, index) in coordinates" :key="index" ref="marker" :lat-lng="coord">
  <MoleculeMapMarkerPopup :item="item" />
</l-marker>

MapMarkerPopup.vue

<l-popup class="map-popup" :auto-close="false">
  <!-- Customize the content of the popup here -->
  <AtomImage v-if="item.image" :src="item.image" class="img" :alt="item.title" data-testid="source-img" />
  <div class="content">
    CONTENT COMES HERE
  </div>
</l-popup>

That stripped code above results to this: When popup is opened

Once the user zooms out the clusters gets "redrawn" and all popups are closed by the map: After user zooms out

1

There are 1 best solutions below

0
On

you can use a combination of @popupopen event from Leaflet and a bitof state management

add a ref to poupup elemnt

<l-marker v-for="(coord, index) in coordinates" :key="index" ref="marker" :lat-lng="coord">
  <MoleculeMapMarkerPopup :item="item" ref="popup" />
</l-marker>

use a state var to track open popup

<template>
  <l-map id="myMap" ref="mapElement" @popupopen="handlePopupOpen" ...>
    <!-- other map components -->
  </l-map>
</template>

<script>
export default {
  data() {
    return {
      openPopupItem: null,
    };
  },
  methods: {
    handlePopupOpen({ popup }) {
      this.openPopupItem = popup.options.item;
    },
  },
};
</script>

receive 'openPopupItem' as a prop in your MapMarkerPopup.vue

<template>
  <l-popup class="map-popup" :auto-close="false" :open="shouldOpenPopup">
    <!-- Popup content -->
  </l-popup>
</template>

<script>
export default {
  props: {
    item: Object,
    openPopupItem: Object,
  },
  data() {
    return {
      shouldOpenPopup: false,
    };
  },
  watch: {
    openPopupItem(newVal) {
      this.shouldOpenPopup = this.item === newVal;
    },
  },
};
</script>

at last we passs openPopupItem to its child comp

<l-marker v-for="(coord, index) in coordinates" :key="index" ref="marker" :lat-lng="coord">
  <MoleculeMapMarkerPopup :item="item" :openPopupItem="openPopupItem" ref="popup" />
</l-marker>

:)