crevulus
crevulus

Reputation: 2418

VueJS: Not printing data returned in method

I'm successfully getting data into the console. When I try to print that data to the page by calling the method in double moustache braces it doesn't appear on screen. All other data in template appears just fine.

Template:

<template>
   <div>
      <div v-for="data in imageData" :key="data.id">
      <div class="card">
        <img :src="data.source" :alt="data.caption" class="card-img" />
        <div class="text-box">
          <p>{{ moment(data.timestamp.toDate()).format("MMM Do YYYY") }}</p>
          <p>{{ data.caption }}</p>
// The Geocoding method is the problem
          <p>{{reverseGeocode(data.location.df, data.location.wf)}}</p>
        </div>
      </div>
    </div>
  </div>
</template>

Method:

methods: {
    reverseGeocode: (lat, long) => {
      fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
          ).then((res) =>
              res.json().then((data) => {
      console.log(data.results[0].formatted_address); // works fine
      return data.results[0].formatted_address;
        })
      );
    },
  },

Here's the image data I'm getting in props Here's the image data I'm getting in props

Upvotes: 2

Views: 1124

Answers (3)

Ayudh
Ayudh

Reputation: 1763

You should change your approach to the following:

Do all requests in the created() lifecycle method and store the results in a data attribute then iterate over the data attribute. The created() lifecycle method executes before the DOM is mounted so all data fetching APIs should be called there. FYR: https://v2.vuejs.org/v2/guide/instance.html

Please also refer to Vue.js - Which component lifecycle should be used for fetching data?

Upvotes: 0

Abarth
Abarth

Reputation: 176

The above I think is correct as well, but I would push for async

async reverseGeocode(lat, long) {
   const response = await fetch(
                `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
            );
   const data = response.json();
   return data.results[0].formatted_address;
}

Upvotes: 0

Vallemar
Vallemar

Reputation: 333

Your problem is a common problem when you start making requests in JavaScript.

The date requests are asynchronous so the method cannot return a value after the execution of the method has finished.

Imagine the following call stack:

  1. Start method.
  2. Throw fetch. <- Asynchronous
  3. Finish method.
  4. Fetch ends.

You are trying to do a return in step 4 and it should be in 3.

To solve this you should use async with await. You could also solve it by making a component and passing the data (this is my favorite since you are using vue).

Component parent

<template>
    <div>
        <component-card v-for="data in imageData" :key="data.id" :dataItem="data">
        </component-card>
    </div>
</template>

Child component

<template>
    <div class="card">
        <img :src="dataItem.source" :alt="dataItem.caption" class="card-img" />
        <div class="text-box">
            <p>{{ moment(dataItem.timestamp.toDate()).format("MMM Do YYYY") }}</p>
            <p>{{ dataItem.caption }}</p>
            <p>{{formattedAddress}}</p>
        </div>
    </div>
</template>
<script>
export default {
    props: {
        dataItem: {
            type: {},
            default: () => ({})
        }
    },
    data() {
        return {
            formattedAddress: ""
        };
    },
    created() {
       this.reverseGeocode(this.dataItem.location.df, dataItem.location.wf) 
    },
    methods: {
        reverseGeocode(lat, long) {
            fetch(
                `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
            ).then(res =>
                res.json().then(data => {
                    console.log(data.results[0].formatted_address); // works fine
                    this.formattedAddress = data.results[0].formatted_address;
                })
            );
        }
    }
};
</script>

I have not tried it, surely some things are missing but the template should be that.

Upvotes: 1

Related Questions