vue-mapbox icon indicating copy to clipboard operation
vue-mapbox copied to clipboard

Map layers do not render again past initial load

Open vbuzzegoli opened this issue 4 years ago • 2 comments

I am using VueMapbox (0.4.1) to utilize Mapbox GL in a Vue project.

<template>
  <MglMap
    :accessToken="accessToken"
    :mapStyle.sync="mapStyle"
    :repaint="true"
    @load="onMapLoaded">
    <MglMarker
      :coordinates="someCoordinates"
      class="map-marker-wrapper">
      <div
        slot="marker"
        class="map-marker">
      </div>
    </MglMarker>
  </MglMap>
</template>

<script>
import Mapbox from 'mapbox-gl'
import { MglMap, MglMarker } from 'vue-mapbox'
import * as MAP from '@/constants/map'

// Vue-Mapbox documentation: https://soal.github.io/vue-mapbox/guide/basemap.html#adding-map-component

export default {
  name: 'Map',
  components: {
    MglMap,
    MglMarker
  },
  props: {
    someCoordinates: {
      type: Array,
      required: true
    },
    darkMode: {
      type: Boolean,
      required: true
    }
  },
  data () {
    return {
      accessToken: MAP.ACCESS_TOKEN,
      mapbox: null,
      map: null,
      actionsDispatcher: null
    }
  },
  computed: {
    mapStyle () {
      return this.darkMode ? MAP.COLOR_PROFILES.DARK : MAP.COLOR_PROFILES.LIGHT
    }
  },
  created () {
    this.mapbox = Mapbox
  },
  methods: {
    async onMapLoaded (event) {
      this.map = event.map
      this.actionsDispatcher = event.component.actions
    
      await this.actionsDispatcher.flyTo({
        center: this.someCoordinates
      })
    }
  }
}
</script>

On the first load, everything works as expected:

Correct Map

But if I move to a different pseudo-route (say from /#/Map to /#/Profile and back), some of the map layers specified by my mapStyle (roads, city names,..) are not rendered anymore (default layers instead). The map also stops honoring any change of the mapStyle url, even when I specify mapStyle.sync in my template.

Incorrect map

If I hit the browser's reload button it loads the layers as expected as the entire app is reloaded from scratch, but I unfortunately cannot afford to do this by default.

Any ideas are greatly appreciated.

vbuzzegoli avatar Jan 13 '21 01:01 vbuzzegoli

I found a solution, in the example of vue-mapbox, the variable map (this.map) is set by "event.map" which causes an error because the card is not refreshed afterwards.

In my case i just remove that (this.map = event.map) in my onMapLoaded function and this is great.

Have a good day.

Pierre-Saigot avatar Jan 21 '21 10:01 Pierre-Saigot

@Pierre-Saigot is correct. Caching the map in the data of the component as the documentation suggests will cause further refresh attempts to fail as it will get observed by Vue, and therefore no longer point to the actual object reference at play directly.

This can be avoided by caching the map outside of the Vue instance's state so it doesn't get observed.

Example:

<template>
  <MglMap
    :accessToken="accessToken"
    @load="onMapLoaded">
  </MglMap>
</template>

<script>
import Mapbox from 'mapbox-gl'
import { MglMap, MglMarker } from 'vue-mapbox'
import * as MAP from '@/constants/map'

let map = null // could live anywhere where it would not be observed by Vue

export default {
  name: 'Map',
  components: {
    MglMap,
    MglMarker
  },
  data () {
    return {
      accessToken: MAP.ACCESS_TOKEN,
      // map should not be observed, must be cached externally
    }
  },
  beforeDestroy () {
     // do something with the map later if necessary
     const lastPosition = map.getCenter()
  },
  methods: {
    onMapLoaded (event) {
      // cache map
      map = event.map
    }
  }
}
</script>

Not closing this issue yet as the documentation likely needs to be updated accordingly.

vbuzzegoli avatar Jan 22 '21 06:01 vbuzzegoli