3

I am trying to create a map using Leaflet and a Vue component. For some reason the "center: " attribute is not accepting my array coordinates for Latitude and Longitude? When I use it in the template html {{ latlng }} I get an array with the correct coordinates. Any help would be greatly appreciated.

<template>
  <div id="mapContainer">{{ latlng }}</div>
</template>

<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import axios from 'axios';


export default {
  name: "Map",
  data() {
    return {
      map: null,
      latlng: []
    };
  },
  methods: {
    get_lat_lng: function(){
        axios.get('http://127.0.0.1:5000/api/get_latitude')
            .then(res => this.latlng.push(res.data))
        axios.get('http://127.0.0.1:5000/api/get_longitude')
            .then(res => this.latlng.push(res.data))
    }
  },
  created: function(){
      this.get_lat_lng()
  },
  mounted() {

    this.map = L.map("mapContainer", {
        center: this.latlng,
        zoom: 12,
    });
    L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
      attribution:
        '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(this.map);
  },
  beforeDestroy() {
    if (this.map) {
      this.map.remove();
    }
  }
};
</script>

<style scoped>
#mapContainer {
  width: 50vw;
  height: 50vh;
}
</style>
4
  • probably the mounted() method is called before the axios aynchronous call has been completed. You will need to slightly change your logic so the map is created not when mounted but when axios completes. Commented Dec 2, 2019 at 19:58
  • I tried putting the axios calls directly in created(). According to the life cycle diagram, created runs before mounted. Not really sure what is happening here. Commented Dec 2, 2019 at 20:17
  • the sequence of calls is probably this 1.created(), 2.mounted(), 3.axios.then(). Commented Dec 2, 2019 at 20:34
  • 1
    so you need to initialize the map in the axios.then() call Commented Dec 2, 2019 at 20:35

2 Answers 2

2

You have hit a race condition here. The mounted() hook is called before the axios.get asynchronous call have been completed.

The sequence of calls is probably this 1.created(), 2.mounted(), 3.axios.then()

So, you will need to slightly change your logic so the map is initialized when both the creation has completed and the mount hook has been invoked.

Something like this,

add a flag for the mounted call been executed

data() {
  return {
    map: null,
    mounted: false,
    latlng: null
  }

move the map creation code into a method

createMap: function() {
  this.map = L.map("mapContainer", { center: this.latlng ...

at creation go fetch the data, and if already mounted, create the map

created(){
  axios.all([axios.get('http://127.0.0.1:5000/api/get_longitude');
             axios.get('http://127.0.0.1:5000/api/get_latitude')])
    .then((longresp,latresp)=> {
       this.latlng=[latresp.data,longresp.data];
       if (this.mounted) this.createMap()
     })

and then at mounted, check if by chance the data is already available, and if not, set the flag for the creation of the map

mounted() {
 if (this.latlng) this.createMap()
 this.mounted = true;
 ...
Sign up to request clarification or add additional context in comments.

2 Comments

This should be the selected answer over mine. Very easy to understand and this one also keeping the compatibility with the API.
Thank you PA. Very helpful.
1

Create the Map when the data is available ( when axios promise is successful ).

More on promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Use a indicator until content not ready.

I think there is no need for separate requests, so i merged the 2 axios requests. Your backend should return the [lat, long] array.

<template>
    <div>
        <!--
         I also added a simple loading indicator,
         obviously you can go for something more fancy like a spinner
        --!>
        <p v-if="loading">Loading...</p>
        <div id="mapContainer">{{ latlng }}</div>
    </div>
</template>

<script>
    import "leaflet/dist/leaflet.css";
    import L from "leaflet";
    import axios from 'axios';

    export default {
        name: "Map",
        data() {
            return {
                map: null,
                loading: false,
                latlng: []
            };
        },
        methods: {
            setupLeafletMap: function () {

                this.map = L.map("mapContainer", {
                    center: this.latlng,
                    zoom: 12,
                });

                L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
                    attribution:
                        '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                }).addTo(this.map);

            }
        },
        mounted() {

            this.loading = true;

            axios.get('http://127.0.0.1:5000/api/get_latlng').then(res => {

                this.latlng = res.data;
                this.setupLeafletMap();  // Creating the Map here ensures that data is already loaded from server side

                this.loading = false;

            }).catch(error => console.error(error));

        },
        beforeDestroy() {
            if (this.map) {
                this.map.remove();
            }
        }
    };
</script>

<style scoped>
    #mapContainer {
        width: 50vw;
        height: 50vh;
    }
</style>

1 Comment

I'm glad that this was helpful! PA's answer goes into a bit more detail, also keeps API compatibility please select that one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.