2

I have a backend API using a MySQL database, I am trying to retrieve data from my database and use the data to plot the latitude and longitude on a map using the google maps API. I am using the Gmaps API with Nuxt JS.
I have tried to do this with the code below but I have encountered an error which says

"Cannot read properties of undefined (reading 'coordinates')"

enter image description here

I am trying to retrieve my data with the below code

async function getData(){
  const {data} = await this.$axios.$get('http://localhost:8000/api/parkingPlace');
  console.log(data)
  return {data}
}

and then pass this data into my Gmaps.

export default {
  data() {
    return {
      currentLocation: {},
      circleOptions: {},
      parkingPlaces: getData(),

Which I can then use to retrieve my values of latitude and longitude and plot these as pins onto my map.

<template>
  <div class="min-h-screen relative max-6/6" >
    <GMap class="absolute inset-0 h-100% bg-blue-400"
      ref="gMap"
      language="en"
      :cluster="{options: {styles: clusterStyle}}"
      :center="{lat:parkingPlaces[0].coordinates.lat, lng: parkingPlaces[0].coordinates.lng}"
      :options="{fullscreenControl: false, styles: mapStyle}"
      :zoom="5">

      <GMapMarker
        v-for="location in parkingPlaces"
        :key="location.id"
        :position="{lat: location.coordinates.lat, lng: location.coordinates.lng}"
        :options="{icon: location.free_spots > 0 ? pins.spacefree : pins.spacenotfree}"
        @click="currentLocation = location"
      >
        <GMapInfoWindow :options="{maxWidth: 200}">
          <code>
            lat: {{ location.coordinates.lat }},
            lng: {{ location.coordinates.lng }}
          </code>
        </GMapInfoWindow>
      </GMapMarker>
      <GMapCircle :options="circleOptions"/>
    </GMap>
  </div>
</template>

My whole index.vue class is below where I am trying to do this.

<template>
  <div class="min-h-screen relative max-6/6" >
    <GMap class="absolute inset-0 h-100% bg-blue-400"
      ref="gMap"
      language="en"
      :cluster="{options: {styles: clusterStyle}}"
      :center="{lat:parkingPlaces[0].coordinates.lat, lng: parkingPlaces[0].coordinates.lng}"
      :options="{fullscreenControl: false, styles: mapStyle}"
      :zoom="5">

      <GMapMarker
        v-for="location in parkingPlaces"
        :key="location.id"
        :position="{lat: location.coordinates.lat, lng: location.coordinates.lng}"
        :options="{icon: location.free_spots > 0 ? pins.spacefree : pins.spacenotfree}"
        @click="currentLocation = location"
      >
        <GMapInfoWindow :options="{maxWidth: 200}">
          <code>
            lat: {{ location.coordinates.lat }},
            lng: {{ location.coordinates.lng }}
          </code>
        </GMapInfoWindow>
      </GMapMarker>
      <GMapCircle :options="circleOptions"/>
    </GMap>
  </div>
</template>

<script>
async function getData(){
  const {data} = await this.$axios.$get('http://localhost:8000/api/parkingPlace');
  console.log(data)
  return {data}

export default {
  data() {
    return {
      currentLocation: {},
      circleOptions: {},

      parkingPlaces: getData(),

      // parkingPlaces: [
      //   {
      //     "id": 1,
      //     "name": "Chandler Larson",
      //     "post": "37757",
      //     "coordinates": {
      //       "lng": -51.84,
      //       "lat": -60.02
      //     },
      //     "total_spots": 0,
      //     "free_spots": 0
      //   }
      // ],

      pins: {
        spacefree: "/parkingicongreen3.png",
        spacenotfree: "/parkingiconred3.png",
      },
      mapStyle: [],
      clusterStyle: [
        {
          url: "https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m1.png",
          width: 56,
          height: 56,
          textColor: "#fff"
        }
      ]
    }
  }
}
</script>

I have tried to use Axios to retrieve data from my database and pass this into my google maps to display points on the map.

I am expecting points to appear on my map where data from my database gives the lat and lng.

EDIT 1:

I have now edited some of my code to reflect the changes as suggested by @kissu.

<template v-if="parkingPlaces.length > 0">
  <div class="min-h-screen relative max-6/6" >
    <GMap class="absolute inset-0 h-100% bg-blue-400"
      ref="gMap"
      language="en"
      :cluster="{options: {styles: clusterStyle}}"
      :center="{lat:this.parkingPlaces[0].coordinates.lat, lng: this.parkingPlaces[0].coordinates.lng}"
      :options="{fullscreenControl: false, styles: mapStyle}"
      :zoom="5">

      <GMapMarker
        v-for="location in parkingPlaces"
        :key="location.id"
        :position="{lat: location.coordinates.lat, lng: location.coordinates.lng}"
        :options="{icon: location.free_spots > 0 ? pins.spacefree : pins.spacenotfree}"
        @click="currentLocation = location"
      >
        <GMapInfoWindow :options="{maxWidth: 200}">
          <code>
            lat: {{ location.coordinates.lat }},
            lng: {{ location.coordinates.lng }}
          </code>
        </GMapInfoWindow>
      </GMapMarker>
      <GMapCircle :options="circleOptions"/>
    </GMap>
  </div>
</template>

<script>

// async function getData(){
//   const {data} = await this.$axios.$get('http://localhost:8000/api/parkingPlace');
//   console.log(data)
//   return {data}
// }

export default {

  data() {
    return {
      currentLocation: {},
      circleOptions: {},
      parkingPlaces: [],


      // parkingPlaces: [
      //   {
      //     "id": 1,
      //     "name": "Chandler Larson",
      //     "post": "37757",
      //     "coordinates": {
      //       "lng": -51.84,
      //       "lat": -60.02
      //     },
      //     "total_spots": 0,
      //     "free_spots": 0
      //   }
      // ],

      pins: {
        spacefree: "/parkingicongreen3.png",
        spacenotfree: "/parkingiconred3.png",
      },
      mapStyle: [],
      clusterStyle: [
        {
          url: "https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m1.png",
          width: 56,
          height: 56,
          textColor: "#fff"
        }
      ]
    }
  },

  async fetch(){
    this.parkingPlaces = await fetch('http://localhost:8000/api/parkingPlace').then(res => res.json())
    console.log(this.parkingPlaces)
  }

}

</script>


Result of my console.log(this.parkingPlaces)

enter image description here

EDIT 2

enter image description here

1 Answers1

1

EDIT

You have an object at the end in your this.parkingPlaces as shown by your console.log.
You need the following to access the actual data (as an iterable array).

async fetch() {
  const response = await fetch('http://localhost:8000/api/parkingPlace')
  const json = await response.json()
  this.parkingPlaces = json.data
  console.log(this.parkingPlaces)
}

PS: I also recommend that you use this.$axios.$get shortcuts as showcased here.


What is getData? Put that function into a methods in your export default { rather.
Then, use asyncData() or fetch() hook to fetch the data properly in a Nuxt way, you can call this.getData inside of any of them.

Apply a conditional in your template so that you have a fallback in your template (only needed for fetch()'s lifecycle hook). asyncData is indeed render-blocking.
It can be v-if="parkingPlaces.length", no need for more.
But don't stack both at the same time. ESlint can warn you about that.

Here is an example, there we're using $fetchState.pending to wait until we have our data before iterating over the array. Without that conditional, the template will iterate on something empty and throw the error that you do have.

Indeed, the template section is not clever enough to understand that the data may be async. Hence it is run into a sync approach. Using a conditional is enough to make it aware of the possibility of something being empty (because not fully async fetched).


More info on how to fetch data in Nuxt available here: https://nuxtjs.org/docs/features/data-fetching#the-fetch-hook

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Hi @kissu, getData is my function name which is supposed to return some data from my database. – cookiemonster300 Nov 27 '22 at 17:27
  • @cookiemonster300 why is it outside of the `export default` block was my main question. Put it inside, especially if you are not planning to re-use it anyway. – kissu Nov 27 '22 at 17:28
  • I have adapted my code as mentioned above but I still seem to get the same error ```async fetch(){ this.parkingPlaces = await fetch('http://localhost:8000/api/parkingPlace').then(res => res.json()) }``` – cookiemonster300 Nov 27 '22 at 17:49
  • @cookiemonster300 please edit your whole question to reflect your changes. And don't forget the conditional rendering with the `v-if`. – kissu Nov 27 '22 at 17:51
  • @cookiemonster300 updated back. – kissu Nov 27 '22 at 18:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/249926/discussion-between-cookiemonster300-and-kissu). – cookiemonster300 Nov 27 '22 at 18:20