Search code examples
asynchronousvuejs2promisees6-promiseconditional-rendering

Render li value once I got value from promise


I have a template rendering a list. Two of these values need to be geocoded (starting with lat lon values I want to display the address). I have the values in the console, but the li displays [object Promise]. I understand the render is not synchronous, so I tried to use a flag to show the li only when I have the values, but they never displayed. Basically I understand what the issue is but can't figure out how to rearrange the code to make it work. Here the template:

<div v-if="!isLoading && Object.keys(currentActivity).length > 0">
            <ul
              v-for="value in currentActivity"
              :key="value.id"
              class="listValues"
            >
              <li>Attività:
                <b>{{ value.activity.activityName }} - {{ value.related_activity }}
                </b>
              </li>
              <li>Inizio: <b>{{ value.start }}</b></li>
              <li>Fine: <b>{{ value.end }}</b></li>
              <li>Durata: <b>{{ timeConvert(value.duration) }}</b></li>
              <li v-if="!isGeocoding">Punto di partenza: <b>{{ getAddress(getLatLng(value.starting_point)) }}</b></li>
              <li v-if="!isGeocoding">Punto di arrivo: <b>{{ getAddress(getLatLng(value.arrival_point)) }}</b></li>
              <li>Stato: <b>{{ value.status | stato }}</b></li>
              <li>Distanza: <b>{{ kmCovert(value.travel_distance) }}</b></li>
              <li>Kcal consumate: <b>{{ value.kcal }}</b></li>
              <li>Produzione di Co2: <b>{{ value.co2 }}</b></li>
              <li>Costo: <b>{{ value.cost }} €</b></li>
              <li>Utente:
                <router-link :to="{name: 'SchedaUtente', params: { userId: value.owner.id }}">
                  <b style="text-decoration: underline;">
                    {{ value.owner.username }}
                  </b>
                </router-link>
              </li>
            </ul>
          </div>

I also tried to put the condition in the div (together with !isLoading && Object.keys(currentActivity).length > 0) but in this way the whole list doesn't display. In the data I set, among others, isGeocoding: true The other methods involved:

async mounted() {
    await this.getActivityData()
  },
  methods: {
    getActivityData() {
      this.isLoading = true
      const currentUser = this.$store.getters.currentProfile.user
      this.currentUser = currentUser
      console.log('current activity scheda attivita: ', this.currentActivity)
      console.log('current user scheda attivita: ', currentUser)
      this.isLoading = false
      console.log('is loading after ', this.isLoading)
    },
    getLatLng(sridPoint) {
      const arrayLatLng = sridPoint.replace('SRID=4326;POINT (', '').replace(')', '').split(' ')
      const latLng = { lat: parseFloat(arrayLatLng[1]), lng: parseFloat(arrayLatLng[0]) }
      return latLng
    },
    async getAddress(latLng) {
      await this.$gmapApiPromiseLazy().then(() => {
        const geocodeAddress = new Promise(function(resolve, reject) {
          // eslint-disable-next-line no-undef
          const geocoder = new google.maps.Geocoder()
          geocoder.geocode({ 'location': latLng }, function(results, status) {
            if (status === 'OK') {
              if (results.length > 0) {
                resolve([results[0].formatted_address])
              }
            } else {
              reject(new Error('Couldnt\'t find the location.'))
            }
          })
        })
        geocodeAddress.then(function(address) {
          console.log(address)
          this.isGeocoding = false
          console.log('geocoding: ', this.isGeocoding)
          return address
        })
      })
    },

In this way the two addresses never display, if I remove the flag isGeocoding, as I said before, I got the values in the console but the [object Promise] in the template. Can someone please help? Thanks x


Solution

  • This is how I got it working:

    1. I've added reactive data attributes to represent the starting and arrival addresses and two more const to have more readable code, so in data:
    startingAddress: null,
    arrivalAddress: null,
    start: null,
    end: null
    
    1. I’ve referenced these attributes, instead of the promises, within the template:
    <li v-if="startingAddress">Punto di partenza: <b>{{ this.startingAddress }}</b></li>
    <li v-if="arrivalAddress">Punto di arrivo: <b>{{ this.arrivalAddress }}</b></li>
    

    In this way I could get rid of the boolean flag isGeocoding, since I can simply check for the individual address attributes.

    1. I’ve then invoked the getAddress promises within the getActivityData function, instead of from within the template. I could then respond to those promises to set the corresponding address attributes.
    async getActivityData() {
          this.isLoading = true
          const currentUser = this.$store.getters.currentProfile.user
          this.currentUser = currentUser
          console.log('current activity scheda attivita: ', this.currentActivity)
          this.start = this.getLatLng(this.currentActivity.activity.starting_point)
          this.end = this.getLatLng(this.currentActivity.activity.arrival_point)
    
          console.log('PROMISE',this.getAddress(this.start))
          const [startingAddress, arrivalAddress] = await 
          Promise.all([this.getAddress(this.start), this.getAddress(this.end)])
    
          this.startingAddress = startingAddress
          this.arrivalAddress = arrivalAddress
    
          this.isLoading = false
        },
    

    I've called this function in mounted as so:

    async mounted() {
      await this.getActivityData()
    },
    
    1. I made the other two functions more reusable, as so:
    getLatLng(sridPoint) {
       const arrayLatLng = sridPoint.replace('SRID=4326;POINT (', '').replace(')', '').split(' ')
       const latLng = { lat: parseFloat(arrayLatLng[1]), lng: parseFloat(arrayLatLng[0]) }
       return latLng
    },
    getAddress(latLng) {
       return this.$gmapApiPromiseLazy().then(() => {
       const geocodeAddress = new Promise(function(resolve, reject) {
         const geocoder = new google.maps.Geocoder()
         geocoder.geocode({ 'location': latLng }, (results, status) => {
           if (status === 'OK') {
             if (results.length > 0) {
               resolve(latLng = results[0].formatted_address)
             } else {
               reject(new Error('Couldnt\'t find the location.'))
             }
           }
         })
       })   
       return geocodeAddress.then(function(address) {
         console.log(address)
         return address
       })
     })
    }
    

    Notice the two returns at the start and at the end of the getAddress function, that I was previously missing:

    • return this.$gmapApiPromiseLazy()....

    • return geocodeAddress....

    x