Google Maps from CDN problem



  • What is the process for adding scripts from CDNs in v0.15.x projects? In particular, I’m trying to add Google Maps. Adding a script tag in index.template.html does not work. Thanks in advance.



  • You should use a quasar plugin and a promise. quasar new plugin google

    Take a look at this post



  • After adding a new plugin and creating an externalApi.js file as suggested in that link, I’m getting a dependency not found error. Any idea why this might be?



  • Please also tell us which dependency could not be found.
    Paste your plugin file here and also the error message.

    If you are only interested in loading Google Maps, you could also take a look at https://github.com/Akryum/vue-googlemaps

    This uses a very similar aproach to load the libary like @benoitranque took, but with the added benefit that it wraps the maps API in Vue components.
    To use this you would first run yarn add vue-googlemaps to add the dependencies.
    Then create a new Quasar plugin with quasar new plugin maps.
    In the newly generated file add the following (make shure to add your api key:

    import 'vue-googlemaps/dist/vue-googlemaps.css'
    import VueGoogleMaps from 'vue-googlemaps'
    
    export default ({ app, router, Vue }) => {
      Vue.use(VueGoogleMaps, {
      load: {
        apiKey: 'your-google-api-key',
        libraries: ['places'],
      },
    })
    }
    
    

    Now in your components you can use the <googlemaps-*> components to create your map:

    <googlemaps-map
      :center.sync="center"
      :zoom.sync="zoom"
      :options="mapOptions"
      @idle="onIdle"
      @click="onMapClick">
    
      <!-- User Position -->
      <googlemaps-user-position
        @update:position="setUserPosition"
      />
    
      <googlemaps-marker
        v-for="marker of markers"
        :key="marker._id"
        :label="{
          color: marker === currentmarker ? 'white' : 'black',
          fontFamily: 'Material Icons',
          fontSize: '20px',
          text: 'star_rate',
        }"
        :position="marker.position"
        @click="selectMarker(marker._id)"
      />
    </googlemaps-map>
    

    If you are interested, take a look at https://github.com/Akryum/vue-googlemaps/blob/master/src/lib-loader.js
    This is the code to laod the lib. You will notice, it is very similar to @benoitranque solution.
    When the load function is run, a new <script> tag gets created which is then populated with the needed information and appended to the window object.



  • @a47ae Thanks for your reply. I’m familiar with the Vue Google Maps components, but I don’t think that approach will work for my use case as I’m needing to integrate custom map code from Algolia.

    As @benoitranque suggested, I created an externalApi.js file which includes the code from his post. I then created a new plugin called ‘google’ and registered it with Quasar. This plugin uses the import command as referenced in the ‘main.js’ section of his post. This causes the dependency error. Quasar can’t find externalApi. Being relatively new to Quasar/Vue, I’m probably just overlooking something.

    This is frustrating because I can include the script in the index.html file of a stock Vue installation and it works fine. It would be helpful if Quasar behaved in a similar way.



  • let me write up a newer version or this, self contained, for use in 0.15



  • @atelier Sure you can’t use this? You can pass whatever Google Maps option you need as a prop named options to <GoogleMapsMap> So you could fetch the options from algolia and jsut pass them. 🙂

    See this line in the src: https://github.com/Akryum/vue-googlemaps/blob/master/src/components/Map.vue#L126

    @benoitranque Nice! During my eesarch for solutions I found react-load-script
    Maybee you could create something similar for Vue.js or even as QLoadScript component? 😃
    Should be relative straightforward for your current code.



  • @a47ae yeah the idea was an async loadscript util. So if you have a large app and say use google maps in only one page, you only want to load google api when that page is visited.
    @atelier here is a v0.15 plugin

    // import something here
    
    // leave the export, even if you don't use it
    export default ({ app, router, Vue }) => {
      // something to do
      let 
        id = 'google-cdn' // must be unique
        url = 'https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxxx'
    
    
      /*
       Usage in component
        
        async mounted () {
          await this.$google
          // google now loaded
        }
        
       Alternative 
    
        methods {
          async initGmaps () {
            await this.$google
            // google now loaded
          }
        }
       */
    
      this.$google = new Promise((resolve, reject) => {
        if (document.getElementById(id)) {
          console.error(`Error loading ${url} async: ${id} is not unique`)
          return
        }
        let script = document.createElement('script')
        script.src = url
        script.async = true
        script.id = id
        script.onload = () => {
          resolve()
        }
        script.onerror = (err) => {
          reject(err)
        }
        document.body.appendChild(script)
      })
    }
    


  • @benoitranque This is still not working. I added your code to a new plugin and registered it with Quasar, however I’m getting undefined errors for ‘google’ and ‘map’ when loading the page with the Algolia code.

    I don’t know if this will be of any help, but here is the code I’m trying to implement:

    https://www.algolia.com/doc/tutorials/geo-search/displaying-results-in-google-map/

    I really appreciate your efforts in helping to resolve this.



  • After implementation of my code, use this is any component:

        async mounted () {
          await this.$google
          // google now loaded
          console.log(google)
        }
    

    edit: please confirm whether google is loading properly using this

    then use the native google maps implementation



  • I tried it in a couple different components and I’m getting a ‘google’ is not defined error.



  • Couple errors on my side, apologies. This should work

    // import something here
    var promise = null
    // leave the export, even if you don't use it
    export default ({ app, router, Vue }) => {
      // something to do
      let 
        id = 'google-cdn' // must be unique
        url = 'https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxxx'
    
    
      /*
       Usage in component
        
        async mounted () {
          await this.$google()
          // google now loaded
        }
        
       Alternative 
    
        methods {
          async initGmaps () {
            await this.$google()
            // google now loaded
          }
        }
       */
    
      Vue.prototype.$google = function () {
        if (!promise) {
          promise = new Promise((resolve, reject) => {
            if (document.getElementById(id)) {
              console.error(`Error loading ${url} async: ${id} is not unique`)
              return
            }
            let script = document.createElement('script')
            script.src = url
            script.async = true
            script.id = id
            script.onload = () => {
              resolve()
            }
            script.onerror = (err) => {
              reject(err)
            }
            document.body.appendChild(script)
          })
        }
        return promise
      }
    }
    

    edited…



  • No luck. I’m still getting ‘google’ not defined error. FYI, I did have to add ‘let =’ before url to define that variable. Not sure if that is affecting your logic.



  • Am I supposed to change ‘google-cdn’ with some other value?



  • I just checked from a brand new Quasar project and @benoitranque solution works fine.
    You first have to create a file src/plugins/maps.js (or run quasar new plugin maps) and add the following:

    let promise = null
    
    export default ({ app, router, Vue }) => {
      let
        id = 'google-cdn',
        url = 'https://maps.googleapis.com/maps/api/js?key=<add-your-key-here>'
    
      Vue.prototype.$google = function () {
        if (!promise) {
          promise = new Promise((resolve, reject) => {
            if (document.getElementById(id)) {
              console.error(`Error loading ${url} async: ${id} is not unique`)
              return
            }
            let script = document.createElement('script')
            script.src = url
            script.async = true
            script.id = id
            script.onload = () => {
              resolve()
            }
            script.onerror = (err) => {
              reject(err)
            }
            document.body.appendChild(script)
          })
        }
        return promise
      }
    }
    
    

    Then add 'maps' to the plugins array in quasar.conf.js

    After that edit the src/pages/index.vue and add the following:

    <template>
      <q-page class="flex flex-center">
        <div id="map"></div>
      </q-page>
    </template>
    
    <style>
    #map {
      height: 400px;
      width: 90%;
    }
    </style>
    
    <script>
    export default {
      name: 'PageIndex',
    
      data () {
        return {
          map: null
        }
      },
    
      methods: {
        initMap () {
          this.map = new window.google.maps.Map(document.getElementById('map'), {
            center: {lat: -34.397, lng: 150.644},
            zoom: 8
          })
        }
      },
    
      async mounted () {
        await this.$google()
        this.initMap()
      }
    }
    </script>
    
    

    Now if you run quasar run dev the browser should open and after a while you should see a map.

    If this does not work for you I can also send you the source files.
    From there it should be easy to adapt the Algolia tutorial. 🙂



  • @a47ae I was able to display a map with the code you provided, but I’m unable to get the Algolia code to work without errors. I’m sure the problem is tied to my limited coding skills. I’ll see if I can figure it out otherwise I may have to resort to a stock Vue approach. Thanks so much to you and @benoitranque for all your help. I really appreciate the effort. I don’t want to waste any more of your time on this.



  • Yeah the Algolia stuff is a bit tricky because the tutorial is intended for plain websites and not Vue.js applications.

    You may wan to take a look at https://community.algolia.com/vue-instantsearch/ which is the official instantsearch plugin for Vue.js

    To be honest I don’t think that it is easier in plain Vue.js, because Quasar is not really different than a plain Vue.js app.

    If you have concrete questions do not hesitate to ask them, we are always glad to help. 🙂



  • The frustrating thing is that the Algolia code works fine on a stock Vue installation after adding the Google API script to the index.html file. It also works great in a Nuxt installation. In fact, Nuxt has a very elegant way of adding external dependencies through its config file. Would love to see something like that for Quasar!



  • That is strange, If it works in a project generated by vue-cli it almost definitely should work in Quasar without much adaptations.
    The only difference is how you load the libraries (btw. also in plain Vue.js it is not recommended to load libraries by adding a script tag, because then you load this library on every page).

    If you want post your code and I can take a look at it when I have some spare time. 🙂