Building A Custom Google Map Component With Vue.js
The Google Maps API is a great service for displaying location-based data such as marker points. While building a web app with Vue.js recently, we wanted to add several Google Map components to the page, each displaying a different set of data. We decided to create our own custom GoogleMap component, and this is how we built it.
Setting up access to Google Maps
The first thing we need to do to add a Google Map to our app is to get an API key. To do so, follow the steps outlined on the Google Developer’s ‘Get API Key’ page. You will need to create a new project (or add to an existing one), and can then use the Credential Wizard to generate your key. The API we will be using is the ‘Google Maps JavaScript API’.
Once you’ve got your key, we’ll need to add Google Maps to our Vue.js project. Add the following line to your index.html file, adding the new API key in the appropriate place.
<script src=”https://maps.googleapis.com/maps/api/js?key=[YOUR API KEY HERE]” ></script>
This gives us access to a global google object, which will be used in our component to create the map.
Creating the component
Now for the fun part! Start by creating a new .vue file to hold our map component. Try to avoid calling it ‘Map’, as this is a reserved HTML tag and may cause problems when you want to include the component on a page. Instead, we called ours ‘GoogleMap’ but you can name it whatever you prefer.
Base template
Let’s start by creating the template which will hold our map component. The markup for this section is very simple, as all we need is a div which will contain the Google Map.
<template><div class="google-map" :id="mapName"></div></template>
With the expectation that this component will be reused in multiple places in our app, we’ve included a dynamic ID attribute. (Note here the shorthand :id syntax instead of the fullv-bind:id). We’ll want to pass this into our component via a prop, with some text transformation for ease of readability. Our <script> for this component therefore reads:
<script>export default {name: 'google-map',props: ['name'],data: function () {return {mapName: this.name + "-map",}}};</script>
We’ll add a little styling in anticipation of our map component to finish off the basic template creation.
<style scoped>.google-map {width: 800px;height: 600px;margin: 0 auto;background: gray;}</style>
Great! We can now add this component in the app by importing it and adding the following markup to the containing page.
<google-mapname="example"></google-map> Showing the map
This component isn’t very helpful to us yet though, because it doesn’t actually show a map! All we can currently see is a grey square… so let’s fix that.
For the script which adds the map, we’re going to hook into the mounted event. This function is called once the template has been loaded, and means that we can interact with the empty div we previously created.
We add the mounted function as a child of our default exported object. Within this, we now create our map by using new google.maps.Map This function takes an element (our map div), and an option object. At minimum, the options you will need to include is a zoom level and a LatLng co-ordinate for the centre of the map.
export default {name: 'google-map',props: ['name'],data: function () {return {mapName: this.name + "-map",}},mounted: function () {const element = document.getElementById(this.mapName)const options = {zoom: 14,center: new google.maps.LatLng(51.501527,-0.1921837)} const map = new google.maps.Map(element, options);}}; Adding markers
Can you figure out where our map is centred? Probably not, because we haven’t added any markers yet!
Start by creating an object which contains our marker coordinate locations, and return it from our data function. You could structure this in any number of ways, but for clarity we’ve created an array of coordinate objects, each which contains a latitude and longitude property. In the future, this data could be passed in via props, to display multiple maps each with different marker points.
data: function () {return {mapName: this.name + "-map",markerCoordinates: [{latitude: 51.501527,longitude: -0.1921837}, {latitude: 51.505874,longitude: -0.1838486}, {latitude: 51.4998973,longitude: -0.202432}]}}
Inside our mounted function we’ll now loop through each of these coordinates. For each one, we’ll create a LatLng object for the marker, and then add the marker to the map we previously created.
this.markerCoordinates.forEach((coord) => {const position = new google.maps.LatLng(coord.latitude, coord.longitude);const marker = new google.maps.Marker({ position,map});});
You’ll now see three markers appear on your map — how exciting! However, if you’d chosen points which were further apart (eg. in different countries), the map in its current state would not display them all. To address this issue, we can use a Google Maps feature called LatLngBounds These boundaries define the edges of the map to be displayed, and can be updated to take marker position into consideration.
Start by creating a new bounds variable at the top of our mounted function.
const bounds = new google.maps.LatLngBounds();
For each marker, we then extend the previously set bounds and update our map accordingly. This overrides the zoom option from earlier, so we can remove this. We’ll also update the map centre to pull its position from our marker data.
Our complete mounted code now reads as follows:
mounted: function () {const bounds = new google.maps.LatLngBounds(); const element = document.getElementById(this.mapName)const mapCentre = this.markerCoordinates[0]const options = {center: new google.maps.LatLng(mapCentre.latitude, mapCentre.longitude)} const map = new google.maps.Map(element, options); this.markerCoordinates.forEach((coord) => {const position = new google.maps.LatLng(coord.latitude, coord.longitude);const marker = new google.maps.Marker({ position,map}); map.fitBounds(bounds.extend(position))});} Dynamic map and markers
So far we have created a map and added some markers to it, all within the Vue.js mounted lifecycle event. Unfortunately, this means that we won’t have access to the map or markers after the component has been mounted. Instead, we want to modify our code a little to make our map dynamic.
In Vue.js, dynamic objects are returned by the data function, so this is where we will be storing our map properties. Below the markerCoordinates object, create three new entries, for map, markers, and bounds
data: function () {return {mapName: this.name + "-map",markerCoordinates: [{latitude: 51.501527,longitude: -0.1921837}, {latitude: 51.505874,longitude: -0.1838486}, {latitude: 51.4998973,longitude: -0.202432}],map: null,bounds: null,markers: []}}
These can be empty to start with as we will initialised them within the mounted event. Here, instead of creating new constants, we will need to update our code to instead reference the appropriate instance object. For example, our map creation line should now read:
this.map = new google.maps.Map(element, options);
We also want to add each new marker to the this.markers object, so that we can reference them at a later stage if necessary.
Our final code is as follows:
<template><div class="google-map" :id="mapName"></div></template> <script>export default {name: 'google-map',props: ['name'],data: function () {return {mapName: this.name + "-map",markerCoordinates: [{latitude: 51.501527,longitude: -0.1921837}, {latitude: 51.505874,longitude: -0.1838486}, {latitude: 51.4998973,longitude: -0.202432}],map: null,bounds: null,markers: []}},mounted: function () {this.bounds = new google.maps.LatLngBounds(); const element = document.getElementById(this.mapName)const mapCentre = this.markerCoordinates[0]const options = {center: new google.maps.LatLng(mapCentre.latitude, mapCentre.longitude)} this.map = new google.maps.Map(element, options); this.markerCoordinates.forEach((coord) => {const position = new google.maps.LatLng(coord.latitude, coord.longitude);const marker = new google.maps.Marker({ position,map: this.map}); this.markers.push(marker)this.map.fitBounds(this.bounds.extend(position))});}};</script> <style scoped>.google-map {width: 800px;height: 600px;margin: 0 auto;background: gray;}</style>
Voila!
There’s a lot more we could do with our map, such as adding & removing markers, custom SVG icons, scrolling to anchor points on the page, map styling and more. For now though, that’s where we’ll be leaving this tutorial.
If you use these steps to create your own GoogleMap component in Vue.js, we would love to see any examples!
Tech at Founders Factory builds businesses that will define the future. We are engineering polyglots, solution hackers and prototyping visionaries. Join Us.
No comments:
Post a Comment