How to use Google Map API in Nuxt Js?

This is my code below to fetch API in Nuxt.Js. I have written the code that should be used to call an API, but I am not getting the results. I am not getting any resources regarding this as well.

async created(){
  const config = {
    headers : {
      Accept : "application/json"
    }
  };
  try{
    const result = await axios.get(`https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap`, config);
    console.warn(result);
    //this.users = result.data;
  }
  catch (err){
    console.warn(err);
  }
},

Upvotes: 3

Views: 4168

Answers (1)

Kalnode
Kalnode

Reputation: 11306

Use the official GM NPM loader + diy Nuxt plugin

There's an official npm loader for the Google Maps JS API:

https://developers.google.com/maps/documentation/javascript/overview#Loading_the_Maps_API

https://www.npmjs.com/package/@googlemaps/js-api-loader

Below is how I have it implemented in Nuxt (2.15.7).

Side note: Yes, this places your API key client side, which in some contexts (e.g. internal team tools) is fine. For public production deployment, you probably want to protect the API key behind a proxy server, and keep any communication with Google occurring only on your server. A proxy server works great for things like Google search and geolocation services, however for map tiles you may never have a map tile server as fast as Google, so you may have to keep an API key on client-side to ensure smooth performance.

1. Install

npm i @googlemaps/js-api-loader

2. Make your own Nuxt plugin

plugins/mapGoogle.client.js

This keeps the Google Map API as a global so you can make use of it in various components (i.e. non-map contexts, like searching Google Places in a form).

import Vue from 'vue'
import { Loader } from '@googlemaps/js-api-loader'

// Store GM_instance as a window object (outside of the Vue context) to satisfy the GM plugin.
window.GM_instance = new Loader({
    apiKey: process.env.GOOGLEMAPSAPIKEY, // This must be set in nuxt.config.js
    version: "weekly",
    libraries: ["places", "drawing", "geometry"] // Optional GM libraries to load
})

Vue.mixin({

    data() {
        return {
            GM_loaded: false, // Tracks whether already GM loaded
            GM_instance: null, // Holds the GM instance in the context of Vue; much more convenient to use *anywhere* (Vue templates or scripts) whereas directly accessing the window object within Vue can be problematic.
            GM_placeService: null, // Optional - Holds the GM Places service
        }
    },

    methods: {

        GM_load() {
            return new Promise( async (resolve, reject) => {

                // Need to do this only once
                if (!this.GM_loaded) {

                    // Load the GM instance
                    window.GM_instance.load()
                    .then((response) => {
                        this.GM_loaded = true
                        
                        // this.GM_instance is what we use to interact with GM throughout the Nuxt app
                        this.GM_instance = response

                        resolve()
                    })
                    .catch(e => {
                        reject(e)
                    })
                } else {
                    resolve()
                }
            })
        },

        // OPTIONAL FUNCTIONS:


        GM_loadPlaceService(map) {
            this.GM_placeService = new this.GM_instance.maps.places.PlacesService(map)
        },

        GM_getPlaceDetails(placeRequest) {
            return new Promise((resolve, reject) => {
                this.GM_placeService.getDetails(placeRequest, (response) => {
                    resolve(response)
                })
            })
        }
    }
})

3. Set env and plugin in nuxt config

nuxt.config.js

Pass your GM key from your .env file and register your new plugin.

export default {
    // ...

    // It's best to keep your GM key where all other keys are: your .env file; however this is inaccessible client-side.
    // Here, we tell Nuxt the specific env's we want to make available client-side.
    env: {
        GOOGLEMAPSAPIKEY: process.env.GOOGLEMAPSAPIKEY
    },

    // Register your new plugin
    plugins: [
        '@/plugins/mapGoogle.client.js',
    ],

    // ...
}

4. Now use the GM plugin anywhere

components/map.vue

Make a map and process clicks on Google Places

<template>
    <div id="map" class="map"></div>
</template>

<script>

export default {
    name: "MapGoogle",

    data() {
        return {
            map: null
        }
    },

    mounted() {

        // This is the actual trigger that loads GM dynamically.
        // Here we run our global GM func: GM_load.
        // Side note; annoyance: As you see, using Vue mixin's, you have functions available from out-of-nowhere. Research alternative to mixin's, especially in Vue3/Nuxt3.
        this.GM_load()
        .then( () => {
            this.initMap()
        })

    },


    methods: {

        initMap() {

            this.map = new this.GM_instance.maps.Map(document.getElementById("map"), {
                center: { lat: 43.682284, lng: -79.401603 },
                zoom: 8,
            })

            this.GM_loadPlaceService(this.map)

            this.map.addListener("click", (e) => {
                this.processClick(e)
            })

        }
    },

    async processClick(e) {
      
        // If clicked target has a placeId, user has clicked a GM place
        if (e.placeId) {
         
            let placeRequest = {
                placeId: e.placeId,
                //fields: ['name', 'rating', 'formatted_phone_number', 'geometry']
            }

            // Get place details
            let googlePlace = await this.GM_getPlaceDetails(placeRequest)
            console.log("googlePlace %O", googlePlace)
        }
            
    }
}
</script>

Upvotes: 5

Related Questions