jperelli / vue2-leaflet-markercluster

markercluster plugin extension for vue2-leaflet package
MIT License
132 stars 55 forks source link

Marker with dynamic content #32

Open mazvv opened 4 years ago

mazvv commented 4 years ago

Hi! I have some problems with markers that has dynamic content. My code is:

<template>
  <div>
    <div class="flex flex-col flex-wrap -mx-2 h-full relative">
      <div class="px-2 min-h-screen">
        <l-map
          ref="map"
          :zoom="zoom"
          :center="placeLatLng"
          :options="mapOptions"
          @update:bounds="updateBounds"
          class="rounded-lg">
          <l-tile-layer
            :url="url"
            :attribution="attribution"></l-tile-layer>
          <vue2-leaflet-markercluster
            :options="{
              chunkedLoading: true,
              animate: false,
            }">
            <l-marker v-for="(car, i) in cars.page.filter(x => x.place)"
              :lat-lng="{ ...car.place }" :key="`place_${i}`"
              :options={car}
              @click="showPopup">
              <l-popup>
                <div class="w-48">
                  <car :car="car" v-if="popups.includes(car.id)"></car>
                </div>
              </l-popup>
              <l-icon>
                <div :class="car.tmr ? 'text-green-700' : 'text-blue-800'">
                  <svgicon name="pin" class="w-6"
                    color="#ffffff inherit inherit"></svgicon>
                </div>
              </l-icon>
            </l-marker>
          </vue2-leaflet-markercluster>
        </l-map>
      </div>
    </div>
  </div>
</template>
.....
showPopup: function (e) {
    const { car } = e.target.options
    if(!this.popups.includes(car.id))
        this.popups.push(car.id)
}

If marker outside the cluster this code works fine, but when cluster spiderify click on marker simply collapse cluster. See image bellow.

Peek 2019-11-03 16-10

holtolee commented 4 years ago

I have the exactly same problem, did you resolve it since you wrote the issue?

Edit: I just found a workaround. Cluster don't like popup with dynamic content, you need to extend your component from LPopup and catch the popupopen event to update it's content like this:

let self = this
let marker = this.$refs[markerRef].mapObject
marker.on('popupopen', function (e) {
    // Get your extended component and call it's "refresh" function mad by you
    let popup = self.$refs[popupRef]
    popup.setOpen(params...)
})

To extend from LPopup just add this to your component:

import * as Vue2Leaflet from 'vue2-leaflet'

export default {
    extends: Vue2Leaflet.LPopup,
    ....

And of course instead of using <l-popup> use your component's name.

Hope it helps other devs. Maybe there is a better fix.

MatthiasAT commented 4 years ago

Hi, I'm facing exactly the same problem and haven't found another solution yet.

@holtolee: Thanks for your workaround. Unfortunately I'm having problems to figure out where and how to overwrite the popupopen event. I assume, I have to put your code somewhere in my custom popup component that is extending LPopup but don't know how. May I kindly ask you to further explain this, maybe based on an example?

Thank you very much in advance!

Update

Thanks to @holtolee I got I working for me, though a bit different. I'm sure there might still be a better solution, but this worked for me.

New custom popup component:

<template>
...
        <!-- Dynamic content part that caused issues when used in "original" l-popup in a markercluster: -->
    <v-layout column v-if="item.id == currentId">
            ...
        </v-layout>
..
</template>

<script>
import * as Vue2Leaflet from 'vue2-leaflet'

export default {
    extends: Vue2Leaflet.LPopup,
    name: 'CustomPopup',
    props: {
        item: Object,
    },
    computed: {
        currentId() {
            return this.$store.state.currentId;     
        },      
    },
}

Parent component with map and markercluster:

<template>
...
  <l-marker-cluster>
       <!-- I did not use the @click event, but defined a custom method for the @popupopen event of the marker: -->
       <l-marker v-for="item in markers" :key="item.id" @popupopen="openMarkerPopup(item)">
      <CustomPopup :item="item" />
       </l-marker>
  </l-marker-cluster>
...
</template>

<script>
...
import CustomPopup from './CustomPopup';
...
export default {
    components: {
        ...
        // LPopup, <-- not required any more
        CustomPopup,
                ...
},
...
methods: {
    openMarkerPopup(item)
    {
                <!-- Setting the currenId that is used as a condition for the dymanic part of the popup content. In my case I don't have
                       to explicitly call marker.openPop() again, as the popup is anyways opening... -->
        this.$store.commit('setCurrentId', id);
    },
},
...
</script>