Created
January 20, 2020 18:57
-
-
Save lancegliser/70b9d51557e3271db37bd2d5486b5849 to your computer and use it in GitHub Desktop.
Provides a single component for displaying a google map using minimum properties using typescript and Vue's composition API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div | |
class="google-map" | |
ref="mapContainer" | |
:style="{ 'min-height': minHeight }" | |
/> | |
</template> | |
<script lang="ts"> | |
import { | |
computed, | |
createComponent, | |
onMounted, | |
reactive, | |
ref, | |
watch | |
} from "@vue/composition-api"; | |
import { | |
getGeocodeResults, | |
getGoogleMap, | |
getGeometryFromGeocodeResult | |
} from "../../../services/Google"; | |
interface IProps { | |
mapConfig: google.maps.MapOptions; | |
minHeight: string; | |
address?: string; | |
} | |
const GoogleMap = createComponent<IProps>({ | |
name: "GoogleMap", | |
props: { | |
mapConfig: { | |
type: Object, | |
default: () => | |
({ | |
clickableIcons: false, | |
streetViewControl: false, | |
backgroundColor: "$303030", | |
mapTypeControl: false, | |
zoomControlOptions: { | |
// style: google.maps.ZoomControlStyle.SMALL // Can not use because google is not defined at load | |
style: 1 | |
}, | |
zoom: 18, | |
mapTypeId: "satellite" | |
} as google.maps.MapOptions) | |
}, | |
minHeight: { | |
type: String, | |
default: "300px" | |
}, | |
address: String | |
}, | |
setup(props: IProps) { | |
return { ...useState(props) }; | |
} | |
}); | |
export default GoogleMap; | |
interface IState { | |
isLoading: false; | |
map?: google.maps.Map; | |
marker?: google.maps.Marker; | |
} | |
function useState(props: IProps) { | |
const mapContainer = ref(null); | |
const state = reactive({ | |
map: undefined, | |
isLoading: false | |
} as IState); | |
const geometry = computed(() => { | |
if (!props.address) { | |
return undefined; | |
} | |
return getGeocodeResults(props.address).then(getGeometryFromGeocodeResult); | |
}); | |
onMounted(async () => { | |
const element: Element | undefined = mapContainer.value ?? undefined; | |
if (!element) { | |
return; | |
} | |
const map = await getGoogleMap(); | |
state.map = new map(element, props.mapConfig); | |
}); | |
watch([geometry, () => state.map], async () => { | |
if (!state.map) { | |
return; | |
} | |
// Remove any current marker | |
if (state.marker) { | |
state.marker.setMap(null); | |
} | |
const value = await geometry.value; | |
if (!value) { | |
return; | |
} | |
state.map.setCenter(value.location); | |
state.marker = new google.maps.Marker({ | |
title: props.address, | |
position: value.location, | |
map: state.map | |
}); | |
}); | |
return { state, mapContainer }; | |
} | |
</script> | |
<style scoped> | |
.google-map { | |
width: 100%; | |
} | |
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios, { AxiosResponse } from "axios"; | |
// Maps | |
import apiLoader from "load-google-maps-api"; | |
export const GOOGLE_API_KEY = "XXXXXXX"; | |
let googleMapApi: typeof google.maps; | |
export const getGoogleMapApi = async () => { | |
if (googleMapApi) { | |
return Promise.resolve(googleMapApi); | |
} | |
return apiLoader({ | |
key: GOOGLE_API_KEY | |
}).then(instance => { | |
googleMapApi = instance; | |
return googleMapApi; | |
}); | |
}; | |
export const getGoogleMap = async (): Promise<typeof google.maps.Map> => { | |
const api = await getGoogleMapApi(); | |
return api.Map; | |
}; | |
export const getGeocodeResults = async ( | |
address: string | |
): Promise<AxiosResponse<{ results: google.maps.GeocoderResult[] }>> => { | |
// We need to ensure the google apis have been loaded. | |
// This makes sure future functions have the correct data shapes available. | |
await getGoogleMapApi(); | |
// TODO caching | |
return axios({ | |
method: "GET", | |
url: `https://maps.googleapis.com/maps/api/geocode/json?address=${address}&key=${GOOGLE_API_KEY}` | |
}); | |
}; | |
export const getGeometryFromGeocodeResult = ( | |
response: AxiosResponse<{ results: google.maps.GeocoderResult[] }> | |
): google.maps.GeocoderGeometry => { | |
const { lat, lng } = response.data.results[0].geometry.location; | |
// There's a bug in the TS description of this API. The returned result is LatLngLiteral, not the class version | |
// @ts-ignore | |
const latLng = new google.maps.LatLng(lat, lng); | |
return { | |
...response.data.results[0].geometry, | |
location: latLng | |
} as google.maps.GeocoderGeometry; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<GoogleMap :address="address" :min-height="minHeight" /> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment