Vue JS props available but data not saving / updating

Hi Vue developers,

I have a VueJS & Google Maps question – could someone please help me figure out why my lat and long coords only work when I physically update them and not when they are passed in as props
I’m using https://www.npmjs.com/package/vue2-google-maps
here is my repo --> https://github.com/JackEdwardLyons/citysearch_app

I’m having trouble because I am fetching the coords and then I try to pass them in as props to the google map, but it isn’t getting picked up, even though the prop value exists.

<template>
  <div class="city-todos">
    <!-- Google Map with Markers -->
    <gmap-map :center="center" :zoom="15" style="width: 100%; height: 500px">
      <gmap-info-window :options="infoOptions" 
                        :position="infoWindowPos" 
                        :opened="infoWinOpen" 
                        :content="infoContent" 
                        @closeclick="infoWinOpen=false">
      </gmap-info-window>
      <gmap-marker :key="i" v-for="(m,i) in markers" 
                   :position="m.position" 
                   :clickable="true" 
                   @click="toggleInfoWindow(m,i)">
      </gmap-marker>
    </gmap-map>


    <div v-show="type == 'city-todos'">
      <ul>
        <li v-for="(todo, inx) in cityTodos" style="text-align: left;">
          <strong>{{ inx + 1 }}: </strong>{{ todo.geometry.location }}</li>
      </ul>
    </div> 

    <div class="modal-card-body--load-wrap has-text-centered"
          v-if="!loaded && type == 'city-todos'" style="text-align: center; margin: 0 auto;">
      <i class="fa fa-spinner fa-spin fa-3x fa-fw"></i>
    </div>

  </div>
</template>

<script>
export default {
  props: [ 'type', 'city', 'cityTodos', 'loaded', 'cityLat', 'cityLng' ],
  data() {
    return {
      center: {
        lat: Number(this.cityLat),
        lng: Number(this.cityLng)
      },
      infoContent: '',
      infoWindowPos: {
        lat: 0,
        lng: 0
      },
      infoWinOpen: false,
      currentMidx: null,
      //optional: offset infowindow so it visually sits nicely on top of our marker
      infoOptions: {
        pixelOffset: {
          width: 0,
          height: -35
        }
      },
      markers: this.cityTodos.map(item => {
        return {
          position: {
            lat: item.geometry.location.lat,
            lng: item.geometry.location.lng
          },
          infoText: item.name
        }
      })
    }
  },
  methods: {
    toggleInfoWindow: function(marker, idx) {

      this.infoWindowPos = marker.position;
      this.infoContent = marker.infoText;

      //check if its the same marker that was selected if yes toggle
      if (this.currentMidx == idx) {
        this.infoWinOpen = !this.infoWinOpen;
      }
      //if different marker set infowindow to open and reset current marker index
      else {
        this.infoWinOpen = true;
        this.currentMidx = idx;

      }
    }
  }
}
</script>

I tried running your code. I don’t see where you’re passing props to the city-todos component.

I see a call to <a @click="showModal(obj.city, 'city-todos')">, but this only seems to pass the city name and the component name to `showModal.

It looks like you’re using an eventbus for global data store, but I’m not seeing where it’s accessed in your city-todos component.

I think that’s the crux of your issue. If it isn’t that, let me know and I’ll dig deeper.

Beautiful site by the way. Looks like it’ll be really nice when finished. Limited selection of cities though (I spent a couple minutes trying to find a city that works before looking in you’re called json file. :stuck_out_tongue: )… might want to add a “city not found” notice.

Thanks looking looking into it. I was thinking of using an event bus later down the track but at the moment I haven’t used the logic anywhere. I am passingly the lat and lng data down through cityLat and cityLmg and the data is “there” but is there something I’m missing?

The data doesn’t seem to register UNLESS you literally have the modal window open, then go into the code and do something random like add a new data property be it anything like foo : “hello” – then with the hot reloading the app will populate with markers ! Try it and let me know if you can reproduce what I mean.

Thanks again! I hope I can get to the bottom of this!

Here’s what I’ve got so far:

It appears Vue is designed this way. Something regarding not reactively updating the data object based on props.

The solution is to force a watcher for the props that need it.


watch: {
    cityLat: function () {
      this.center.lat = Number(this.cityLat);
    },
    cityLng: function () {
      this.center.lng = Number(this.cityLng);
    },
    cityTodos: function () {
      this.markers= this.cityTodos.map(item => {
        return {
          position: {
            lat: item.geometry.location.lat,
            lng: item.geometry.location.lng
          },
          infoText: item.name
        }
      })
    }
  }

Adding those 3 props to the watch list forces your map to redraw.

There is another option: You can use props directly in methods, computed, and even the template. You might be able to refactor to just use the prop directly.

For example, this works for me:

<gmap-map :center="{lat:Number(cityLat), lng:Number(cityLng)}" ...

It might be cleaner as a computed property though. :wink:

Let me know how you get on.

That’s so cool. I had a thought that a watcher might be the way to go but I honestly had no idea how to use a watcher, the docs were a bit basic. Thank you.

Also that’s cool that you can use the prop directly too.

Thanks for the tips. I really appreciate it. Have you been doing any projects in Vue lately?

Mostly, actually. :slight_smile:

Nothing at your scale yet, but some simple ESL games (Here’s a drag&drop shopping cart) and some of the challenges here (Simon game). Vue is so easy to get into.

In almost every case, I was able to focus on something else (shopping cart has a Gulp-based image mixin generator… so I can generate different stores with different objects. Simon is almost entirely SVG-based).

I’m still trying to keep my React skills sharp too, but it feels more “verbose” compared to Vue.

COol. Yeah I want to finish up the data viz cert with Vue js. This road trip project has been a great intro and I’ve learned a so much along the way. I love Vue in comparison to react – to me it just make sense. Although the majority of jobs on the market are for react – hopefully that will change :slight_smile:

I just updated the repo with the changes, Thanks it works well… But the Google Places API is a bit of a let down… The Places Of Interest are things like schools, malls, shops and just boring stuff. I wanted tourist attractions. I have tried it with Parks, which is cool but still it doesn’t capture ALL the parks in the area.

:stuck_out_tongue:

That doesn’t surprise me much. The people living there probably don’t see the cool stuff anymore and they’re the ones updating that info. Everyday life wins. :frowning:

Are there any other sources for stuff like that? Does Lonely Planet or some other travel site have an API worth exploring?

1 Like

Yeah I wanted to use the TripAdvisor API but I never got any confirmation email.
The Lonely Planet API is deprecated.
The Zomato API is also deprecated.

I’m sure there are others out there… I just wanted a cool interactive Map-like experience.

Ah well… I’ll try to add links as well as physical addresses to the Map markers and at least that will make it a bit more engaging.