In this blog post, we'll explore how to build a Vue.js mobile application that integrates with Google Maps using the Ionic Capacitor Google Map Plugin. We'll walk through the code step by step, discussing the code in the script section and the template section of the components used to build this web and mobile application
This application is built using Ionic Framework and Vue JS. We are only using Ionic Framework for the UI components, and those components can be replaced with any vue js compatible UI components
HomePage.vue
Script Section:
Imports:
We start by importing the necessary dependencies and components. We import Ionic framework components from the @ionic/vue
package, and we use the vue package to access the ref function, which enables us to create reactive references. Additionally, we import two custom components, MyMap
and MarkerInfoWindow
, from other files. Lastly, we import Capacitor
from the Capacitor framework, which is used for building cross-platform apps.
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonPopover,
modalController,
} from "@ionic/vue";
import { ref } from "vue";
import MyMap from "./MyMap.vue";
import MarkerInfoWindow from "../components/MarkerInfoWindow.vue";
import { Capacitor } from "@capacitor/core";
Variable Declarations:
We declare two reactive reference variables: markerInfo
and markerIsOpen
. These variables will hold information about the currently selected marker and whether the marker popover is open or closed, respectively.
We define an array called markerData
that holds sample data for markers on the map. Each marker object consists of coordinates
(latitude and longitude), a title
, and a snippet
. You can customize this data by adding more markers as needed.
const markerInfo = ref<any>();
const markerIsOpen = ref<boolean>(false);
// sample data for the map
const markerData = [
{
coordinate: { lat: 37.769, lng: -122.446 },
title: "title one",
snippet: "title one snippet content will be presented here",
},
{
coordinate: { lat: 37.774, lng: -122.434 },
title: "title two",
snippet: "title two snippet content will be presented here",
},
{
coordinate: { lat: 37.783, lng: -122.408 },
title: "title three",
snippet: "title three snippet content will be presented here",
},
// Add more points as needed
];
Functions:
openModal
is an event handler for when a marker is clicked. It creates and presents a modal window using modalController.create()
. The modal displays the MarkerInfoWindow
component, which shows detailed information about the marker
.
mapClicked
is an event listener for when the map is clicked. It simply logs a message to the console, allowing you to perform any desired actions when the map is clicked.
getMarkerInfo
takes a marker
object as input and returns the associated information from the markerData
array. This function is used to retrieve the marker information when a marker is clicked.
markerClicked
is an event handler for when a marker is clicked. It checks if the platform is not native (i.e., running on the web) using Capacitor.isNativePlatform()
. If it is not native, it calls the openModal
function to display the marker information in a modal. This distinction is necessary because the web version doesn't show an info window directly on the map.
const openModal = async (marker: any) => {
const modal = await modalController.create({
component: MarkerInfoWindow,
componentProps: {
marker,
},
initialBreakpoint: 0.2,
breakpoints: [0, 0.2],
backdropBreakpoint: 0,
showBackdrop: false,
backdropDismiss: true,
});
modal.present();
const { data, role } = await modal.onWillDismiss();
};
const mapClicked = () => {
console.log("mapClicked");
};
const getMarkerInfo = (marker: { latitude: number; longitude: number }) => {
return markerData.filter(
(m) =>
m.coordinate.lat === marker.latitude &&
m.coordinate.lng === marker.longitude
)[0];
};
const markerClicked = (event: any) => {
console.log(event);
// only use dialog in web since we doesnt show info window
if (!Capacitor.isNativePlatform()) {
openModal(getMarkerInfo(event));
}
};
Template Section:
The template section defines the structure and layout of our component's HTML template using Ionic Vue components. Let's go through it:
<ion-page>
represents a page within our Ionic app. It provides a container for the entire content.
<ion-header>
contains the header toolbar for the page. We use it to display the app title.
<ion-toolbar>
represents a toolbar within the header. It provides a place for buttons, titles, and other elements.
<ion-title>
displays the title of the page. In this case, it's set to "Vue + Capacitor + Google Maps".
<ion-content>
contains the main content of the page. It provides a scrollable area where we'll display our map and markers.
<my-map>
is a custom component that represents the map. We pass in the markerData
prop to provide the marker information. The component also emits two events: onMapClicked
and onMarkerClicked
, which allow us to handle map and marker clicks, respectively.
<ion-popover>
represents a popover, which is a small overlay window. It takes the isOpen
prop to control the popover's visible status
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>Vue + Capacitor + Google Maps</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<my-map
:markerData="markerData"
@onMapClicked="mapClicked"
@onMarkerClicked="markerClicked"
></my-map>
<ion-popover
:is-open="markerIsOpen"
size="cover"
@did-dismiss="markerIsOpen = false"
>
<ion-content class="ion-padding">
<div>{{ markerInfo?.title }}</div>
</ion-content>
</ion-popover>
</ion-content>
</ion-page>
MyMap.vue
In this section, we'll discuss a new component responsible for rendering the map and markers using the Capacitor Google Maps plugin. We'll explain the script section, which contains the logic, and the template section, which defines the component's structure.
Script Section:
The script section contains the logic and functionality of the custom map component. Let's break it down:
Imports
We import necessary functions from the vue package, such as onMounted, nextTick, ref, and onUnmounted. These functions are used to handle component lifecycle events. We also import the GoogleMap object from the @capacitor/google-maps package, which provides the functionality to interact with the Google Maps plugin.
import { onMounted, nextTick, ref, onUnmounted } from "vue";
import { GoogleMap } from "@capacitor/google-maps";
Props and Emits Declarations
The component defines two parts: props and emits. Props allow us to pass data into the component, and emits enable us to trigger custom events.
In this case, the component expects a prop named markerData
that contains an array of marker objects.
The emits section declares two events: onMarkerClicked
and onMapClicked
. These events will be emitted back to the parent component when a marker or the map is clicked.
// PROPS
const props = defineProps<{
markerData: { coordinate: any; title: string; snippet: string }[];
}>();
// EVENTS
const emits = defineEmits<{
(event: "onMarkerClicked", info: any): void;
(event: "onMapClicked"): void;
}>();
Variable Declarations
mapRef
is a reactive reference variable that holds a reference to the map element in the DOM.
markerIds
is a reactive reference variable that holds an array of marker IDs.
newMap
is a variable that will hold an instance of the GoogleMap object.
const mapRef = ref<HTMLElement>();
const markerIds = ref<string[] | undefined>();
let newMap: GoogleMap;
Lifecycle Hooks
onMounted
is a lifecycle hook that runs when the component is mounted to the DOM. Inside this hook, we use nextTick
to wait for the component to render completely. Then, we call the createMap
function to initialize and render the map.
onUnmounted
is a lifecycle hook that runs when the component is about to be unmounted. Inside this hook, we call the removeMarkers
method of the newMap
object to remove the markers from the map.
onMounted(async () => {
console.log("mounted ", mapRef.value);
await nextTick();
await createMap();
});
// remove markers on unmount
onUnmounted(() => {
console.log("onunmounted");
newMap.removeMarkers(markerIds?.value as string[]);
});
Function Declarations
addSomeMarkers
is an async function that takes an instance of the GoogleMap object as a parameter. It removes any existing markers from the map and adds new markers based on the markerData prop passed to the component.
createMap
is an async function responsible for creating and rendering the map. It first checks if the mapRef
value exists. Then, it uses the Capacitor Google Maps plugin to create a new map instance. It sets the map's ID, the associated DOM element, the API key, and the initial configuration (center and zoom level).
After creating the map, it calls the addSomeMarkers
function to add the markers to the map. Lastly, it sets event listeners to handle marker clicks and map clicks, emitting the appropriate events to the parent component.
const addSomeMarkers = async (newMap: GoogleMap) => {
markerIds?.value && newMap.removeMarkers(markerIds?.value as string[]);
// Plot each point on the map
let markers = props.markerData.map(({ coordinate, title, snippet }) => {
return {
coordinate,
title,
snippet,
};
});
markerIds.value = await newMap.addMarkers(markers);
};
/**
*
*/
async function createMap() {
if (!mapRef.value) return;
// render map using capacitor plugin
newMap = await GoogleMap.create({
id: "my-cool-map",
element: mapRef.value,
apiKey: import.meta.env.VITE_APP_YOUR_API_KEY_HERE as string,
config: {
center: {
lat: 37.783,
lng: -122.408,
},
zoom: 12,
},
});
// add markers to map
addSomeMarkers(newMap);
// Set Event Listeners...
// Handle marker click, send event back to parent
newMap.setOnMarkerClickListener((event) => {
emits("onMarkerClicked", event);
});
// Handle map click, send event back to parent
newMap.setOnMapClickListener(() => {
emits("onMapClicked");
});
}
Template Section
The template section defines the structure of the component's HTML template. Here's a breakdown:
<div>
: Acts as a container for the map component.
<capacitor-google-map>
: Represents the custom map component provided by the Capacitor Google Maps plugin. It accepts a ref attribute (mapRef) to reference the map element and applies inline styles to set the display, width, and height of the map.
<template>
<div>
<capacitor-google-map
ref="mapRef"
style="display: inline-block; width: 100vw; height: 86vh"
>
</capacitor-google-map>
</div>
</template>
In this section, we explored the code for a custom map component that integrates with Capacitor and Google Maps. The script section handles the logic, including the creation of the map, adding markers, and handling events. The template section defines the structure of the component's HTML template, which includes a <div>
container and the <capacitor-google-map>
element provided by the Capacitor Google Maps plugin.
MarkerInfoWindow.vue
In this section, we'll discuss a Vue component that displays marker information. We are using this component in this example for only the Web version of the application since the expected GoogleMaps InfoWindow
is not supported.
We'll explain the template section, which renders the marker data, and the script section, which defines the component's logic.
Template Section:
The template section is responsible for rendering the component's HTML structure. Let's break it down:
<ion-content>
:
This Ionic framework component represents the main content area of the component. It acts as a container for the component's content.
<p>
:
This HTML paragraph element displays the marker information. The content of the paragraph is generated dynamically using the Vue expression JSON.stringify(marker,null,2)
. It uses the JSON.stringify()
function to convert the marker prop into a formatted JSON string with an indentation of 2 spaces.
This was just a placeholder for information that you would want to formation and display for the user to see what marker was just clicked
<template>
<ion-content >
<p>{{JSON.stringify(marker,null,2)}}</p>
</ion-content>
</template>
Script Section:
The script section contains the logic and functionality of the component. Let's go through it:
Imports:
We import the all the Ionic Component from the @ionic/vue package that are needed for this component. This component is used in the template section to represent the content area.
Props and Emits Declarations:
The component defines two parts: props
and emits
. Props allow us to pass data into the component, and emits enable us to trigger custom events.
In this case, the component expects a prop named marker
that contains an object with coordinate
and title
properties.
The emits section declares an event named onDismiss
.
<script setup lang="ts">
import {
IonContent,
} from "@ionic/vue";
// PROPS
const props = defineProps<{
marker: { coordinate: any; title: string };
}>();
const emits = defineEmits<{
(event: "onDismiss"): void;
}>();
Summary:
In this section, we examined the code for a Vue component that displays marker information. The template section renders the marker data using the JSON.stringify()
expression. The script section defines the props and emits declarations.
You can use this component to display the marker information in your Vue application. Customize the template section as needed to achieve the desired layout and styling.
Conclusion:
In this blog post, we examined the code for a custom map component built with Vue, Ionic Capacitor, and Google Maps.
By understanding this code, you now have a foundation for building a Vue mobile application that integrates with Ionic Capacitor and Google Maps. You can customize the component further by adding additional functionality or styling to suit your specific needs.
Remember to properly configure your API key in the createMap
function to ensure that the Google Maps plugin functions correctly. Additionally, feel free to explore the Ionic Capacitor and Google Maps documentation for more advanced features and capabilities.
aaronksaunders / ionic-vue-capacitor-google-maps-app-1
how to use ionic capacitor google map plugin in vue to build mobile app
ionic-vue-capacitor-google-maps-app-1
VIDEO - https://youtu.be/nuVS6nXzRgE
Top comments (4)
This was awesome and exactly what I was looking for! Thank you!
glad you found it helpful
I'm still having issues with your code, and also do a simple integration as ionic tutorial mention. I think the problem is on the google maps key but I have with no restriction in order to test it, also create a new one with restriction on iOS app and the build name of the app. but no look, do you have any recommendation? thank you in advance
do you have downloaded the attached project and it doesn't run? did you follow the steps required when running in an emulator?