Search code examples
google-mapsvue.jsvuejs2infowindowvue-router

vue router's router-link in google maps infowindow


I've got this working fine, except that I've would like instead of <a> tag to use <router-link> tag because of <a> tag unlike <router-link> (which intercepts click event and don't reload the page) reloads a page.

const contentString = `
    <div class="popup">
        <a href="/places/${place._id}">
            <img src="..." />
            <h5>${place.name}</h5>
            <p>${place.gmap.address}</p>
        </a>
    </div>`

const infoWindow = new google.maps.InfoWindow()
infoWindow.setContent(contentString)

If I replace <a> tag for <router-link> it doesn't render to <a> tag as it would if that <router-link> was in vuejs template.


Solution

  • You are right. router-link wasn't rendered because Vue.js got no chance to render it. You can let vue.js render your template at first, get rendered result as string by using innerHTML, and then pass the string to Google Maps API(setContent).

    I'd like to provide 2 solutions here.

    1. The first one is easy to read and understand. Just use it as a normal component, but add v-show="false" in the tag so that it won't appear in your view. (place can be passed as a prop)

      <div id="app">
        <api v-show="false" :place="place"></api>
      </div>
      

      Vue instance:

      new Vue({
        router: new VueRouter({}),
        el: '#app',
        data: {
          place: {
            id: 1,
            name: 'awesome-name',
            address: 'somewhere',
          }
        },
        components: {
          api: {
            props: [ 'place' ],
            template: `
              <div class="api">
              <div class="popup">
                <router-link :to="'/places/' + place.id">
                  <h5>{{place.name}}</h5>
                  <p>{{place.address}}</p>          
                </router-link>
              </div>
              </div>
            `,
          },
        },
        mounted() {
          console.log(this.$children[0].$el.innerHTML);
          //=> <div class="popup"><a href="#/places/1" class=""><h5>awesome-name</h5> <p>somewhere</p></a></div>
        },
      });
      
    2. If you don't want to pollute your html, you can generate the component's vm programmatically by using Vue.extend, create a virtual dom, then mount your vm to created dom.

      const myComp = Vue.extend({
        router: this.$options.router,
        template: `
          <div class="api">
          <div class="popup">
            <router-link to="/places/${this.place.id}">
              <h5>${this.place.name}</h5>
              <p>${this.place.address}</p>          
            </router-link>
          </div>
          </div>
        `,
      });
      const comp = new myComp().$mount(document.createElement('div'));
      console.log(comp.$el.innerHTML);
      //=> <div class="popup"><a href="#/places/1" class=""><h5>awesome-name</h5> <p>somewhere</p></a></div>
      

    I combined solutions above in a fiddle, please have a look.