Search code examples
leafletvuejs3

Using a Vue3 component as a Leaflet popup


This previous SO question shows how we can use a Vue2 component as the content of a LeafletJS popup. I've been unable to get this working with Vue3.

Extracting the relevant section of my code, I have:

<script setup lang="ts">
import { ref } from 'vue'
import L, { type Content } from 'leaflet'
import type { FeatureCollection, Feature } from 'geojson'

import LeafletPopup from '@/components/LeafletPopup.vue'

// This ref will be matched by Vue to the element with the same ref name
const popupDialogElement = ref(null)

function addFeaturePopup(feature:Feature, layer:L.GeoJSON) {
  if (popupDialogElement?.value !== null) {
    const content:Content = popupDialogElement.value as HTMLElement
    layer.bindPopup(() => content.$el)
  }
}
</script>

<template>
  <div class="map-container">
    <section id="map">
    </section>
    <leaflet-popup ref="popupDialogElement" v-show="false">
    </leaflet-popup>
  </div>
</template>

This does produce a popup when I click on the map, but it has no content.

If, instead, I change line 14 to:

    layer.bindPopup(() => content.$el.innerHTML)

then I do get a popup with the HTML markup I expect, but unsurprisingly I lose all of the Vue behaviours I need (event handling, etc).

Inspecting the addFeaturePopup function in the JS debugger, the content does seem to be an instance of HTMLElement, so I'm not sure why it's not working to pass it to Leaflet's bindPopup method. I assume this has something to do with how Vue3 handles references, but as yet I can't see a way around it.

Update 2022-06-09

As requested, here's the console.log output: I've put it in a gist as it's quite long


Solution

  • So just to document the solution I ended up using, I needed to add an additional style rule in addition to the general skeleton outlined in the question:

    <style>
    .leaflet-popup-content >* {
      display: block !important;
    }
    </style>
    

    This overrides the display:none that is attached to the DOM node by v-show=false. It would be nice not to need the !important, but I wasn't able to make the rule selective enough in my experiments.