Vue2Leaflet: vue-marker not displaying on map and map not fully rendering

Description

Steps to Reproduce

  1. installed Vue2Leaflet via npm
  2. created a component called Simple with the following contents (just copied the Simple.vue example and added the style import)

<template>
  <div class="simple">
    <div id="top_div" style="height: 100%">
      <v-map :zoom="zoom" :center="center" style="height: 1000px; width: 1000px">
        <v-tilelayer :url="url" :attribution="attribution"></v-tilelayer>
        <v-marker :lat-lng="marker"></v-marker>       
      </v-map>
    </div>
  </div>
</template>

<script>
 import Vue2Leaflet from 'vue2-leaflet';
 
export default {
  name: 'simple',
  components: {
    'v-map': Vue2Leaflet.Map,
    'v-tilelayer' :Vue2Leaflet.TileLayer,
    'v-marker': Vue2Leaflet.Marker
  },
  data () {
    return {
        zoom: 13,
        center: [47.413220, -1.219482],
        url: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
        marker: L.latLng(47.413220, -1.219482),
    }
  }
}
</script>

<style>
 @import "~leaflet/dist/leaflet.css";
</style>

Expected Results

I expect to see the basic map with marker from the example jsfiddle.

Actual Results

The map appears as a large gray box with a small section with rendered map tiles. My problem looks identical to the one described in #81 , but I do include the leaflet css import.

leaflet

What is strange is that when I resize the window, the map loads as it should, but the marker does not show up. I see in the html that the marker is loaded, however: leaflet2

Browsers Affected

  • [x ] Chrome
  • [x ] Firefox
  • Edge
  • Safari 9
  • Safari 8
  • IE 11

Versions

  • Leaflet: v1.2.0
  • Vue: v2.5.2
  • Vue2Leaflet: v0.0.55

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 24
  • Comments: 47 (6 by maintainers)

Commits related to this issue

Most upvoted comments

I’ve had some similar issues. The marker image issue I solved using info from these threads: https://github.com/PaulLeCam/react-leaflet/issues/255 https://github.com/Leaflet/Leaflet/issues/4849 https://github.com/Leaflet/Leaflet/issues/4968

Basically, sounds like webpack is jacking up the embedded image URLs in the CSS. Throwing this at the top of your main.js may help:

import L from 'leaflet';
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});

I’ve gotten to the point that everything renders after the user resizes but I can’t figure out why I’m getting grey space before that…

I ended up with a hack that helped get everything to render correctly as far as the resize issue goes.

mounted() {
      setTimeout(function() { window.dispatchEvent(new Event('resize')) }, 250);
}

This works down to 200 ms. After that the hack fails. Seems to be some sort of race condition. I tested slower bandwidths and they work fine.

Hello guys, If you are facing such problem with vuetifyjs it’s most probably that the map container is initialized with a small size.

To solve this problem you need to insure that you are loading the map whenever the container is taking the actual size.

For example, if you are using dialog as a container for the map, all what you have to do is using ‘lazy’ prop to insure that the content is rendered on mounted; in other words, the map must be rendered only if the dialog is activated 🎉

<v-dialog v-model="dialog" lazy>
    <l-map
      :zoom="zoom"
      :center="center"
      :style="mapStyle"
      @update:center="centerUpdate"
      @update:zoom="zoomUpdate">
      <l-tile-layer
        :url="url"
        :attribution="attribution"/>
      <l-marker :lat-lng="marker">
      </l-marker>
    </l-map>
</v-dialog>

I hope this will help some one 😄

Quick update for those using vuetify 2.0. The lazy prop does not longer work, but v-lazy works:

v-lazy
  l-map

That will do the trick!

I ended up with a hack that helped get everything to render correctly as far as the resize issue goes.

mounted() {
      setTimeout(function() { window.dispatchEvent(new Event('resize')) }, 250);
}

This works down to 200 ms. After that the hack fails. Seems to be some sort of race condition. I tested slower bandwidths and they work fine.

This work for me!! thanks!!

It should be added in your ~/projectName/src/main.js file when you instantiate your Vue object. I’m also using Vuetify but my main.js file looks like this:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.css'
import "leaflet/dist/leaflet.css"
import "vue-d3-network/dist/vue-d3-network.css"




Vue.use(Vuetify, { theme: {
  primary: '#FF6E40',
  secondary: '#FF9E80',
  accent: '#26A69A',
  error: '#FF3D00',
  warning: '#EA80FC',
  info: '#29B6F6',
  success: '#64DD17'
}})

Vue.config.productionTip = false


new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

Hi @Alhakem , Thank you for the v-tab-item tag! I did not see that we could also use the “lazy” parameter in the v-tab!

Fot the sheet, the solution to rerender the Map works great!!! (I used the solution described at the bottom of this page : http://michaelnthiessen.com/force-re-render/).

I setted a key parameter on the map

<l-map :key="componentKey" ref="refMap" :zoom="10" :center="positionInitiale">

and change the value of the componentKey after cliquing on the button to display the sheet

forceRerender() {
      this.componentKey = this.componentKey + 1;
    },
watch: {
    sheetMap: function(val) {
      if (val == true) {
        this.$nextTick(() => {
          this.$refs.refMap2.forceRerender();
        });
      }
    }
  },

Big thanks to all!

For anyone having problems with vuetify this is the layout which is working for me:

<v-container class="pa-0" fluid fill-height>
        <v-layout>
            <v-flex x12>
                <l-map v-resize="onResize">
                </l-map>
            </v-flex>
        </v-layout>
</v-container>

And on resize I do:

onResize() {
    this.$refs.map.mapObject.invalidateSize();
}

As for the issue with sizing issue in Vuetify: add a class on top of the v-container element :

.i { height: 100%; margin: 0; padding: 0; width: 100%; z-index: 0; }

then go into the vuetify.css and rip out all of the media queries for the .container class. I think its on lines 9301 to 9313.

I have a situation where my <l-map> component is inside a Vuetify stepper, and the problem is that it renders all of the <v-stepper-content> components at once, but then not the map. I’m not that experienced with Vue so maybe this is totally wrong, but it seems to be that the map is pre-initizalied at too-small a size and then when you step to the one with the map, it doesn’t load properly and you get the grey square with the map tiles bunched up in the upper-left-hand corner.

I tried many solutions here but the only one I could get to work was to set a watcher on the variable controlling the stepper and use @azurelogic’s setTimeout-based solution if the stepper variable is the one with the map on it, like this:

Template

<v-stepper v-model="stepper">
    <v-stepper-items>
      <v-stepper-content step="1">
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="primary" @click.native="stepper = 2">Continue</v-btn>
        </v-card-actions>
      </v-stepper-content>
      <v-stepper-content step="2">
            <client-only>
              <l-map id="map" ref="map">
                <l-tile-layer
                  :url="mapInitData.basemap.url"
                  :attribution="mapInitData.basemap.attribution"
                />
              </l-map>
            </client-only>
      </v-stepper-content>
    </v-stepper-items>
  </v-stepper>

Script

import ClientOnly from "vue-client-only";
import { LMap, LTileLayer, LGeoJson } from "vue2-leaflet";
import { Icon } from "leaflet";
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
  iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
  iconUrl: require("@/assets/marker-icon.png"),
  shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});

export default {
  components: {
    LMap,
    LTileLayer,
    ClientOnly
  },
  data() {
    return {
      stepper: 1,
      mapInitData: {
        basemap: {
          attribution:
            '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
          url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        }
      },
      
  watch: {
    stepper: function (val) {
      if (val === 2) {
        this.reloadMap();
      }
    }
  },
  methods: {
    reloadMap: function() {
      console.log("reloading map");
      // this.$refs.map.mapObject.invalidateSize(); // Doesn't work!
      setTimeout(function() { window.dispatchEvent(new Event('resize')) }, 250);
    }
  }
};

Hi, In order to work with SEO, I added a watch function when tab item is selected :

watch: {
    tabSelected: function(tabItem) {
      // Refresh the map of the tab
      this.$nextTick(() => {
        if (this.$refs.refCarteEtablissements) {
          this.$refs.refCarteEtablissements.map(ref => {
            ref.forceRerender();
          });
        }
      });
    }
  },

And I my CarteEtablissement component

<l-map
        :zoom="13"
        :center="positionInitiale"
        :key="componentKey"
        style="z-index:1"
      >
....
</l-map>
 data() {
    return {
       ....
      componentKey: 0,
      ...
    };
  },
 methods: {
    forceRerender() {
      this.componentKey = this.componentKey + 1;
    },
}

And now the map displays correctly and when I show the source of the page, I see the text for the SEO.

Btw, I am using iView UI Library

I was experiencing the same issue of leaflet tile not fully loading on the map. After going through various solutions and hacks suggested above., I finally managed to solve the issue by calling: this.$refs.map.mapObject.invalideSize() inside mounted lifecycle hook. Plus I set a hard-coded height and width of the map’s parent container <div>. And lastly, added the following code inside main.js file

(this step is though added in docs of vue2-leaflet when going through the installing section)

import { L } from 'vue2-leaflet';
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});```