🔭 I’m currently managing a team working on administration features for SafetyCulture
💬 Ask me about strategy, management, frontend, and startups
📫 How to reach me: https://chisnall.io
⚡ Fun fact I believe CSS is a programming language
A lightweight Google Maps plugin for Vue
Home Page: https://xon52.github.io/x5-gmaps
License: MIT License
🔭 I’m currently managing a team working on administration features for SafetyCulture
💬 Ask me about strategy, management, frontend, and startups
📫 How to reach me: https://chisnall.io
⚡ Fun fact I believe CSS is a programming language
How to update polygon paths
when editing the shape by dragging the control points?
Hi, I wanted to give V3 alpha a trial and installed it via npm install x5-gmaps
, the resulting package.json entry looks good: "x5-gmaps": "^1.0.0-alpha.3"
My main.js looks like this:
import {createApp} from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import gmaps from 'x5-gmaps';
const app = createApp(App);
app
.use(router)
.use(store)
.use(gmaps, {
key: 'xXXxxxxXXXXxXXXXXXX'
})
.mount('#app');
And I’m getting serious error messages in the console, my app wouldn’t start anymore. Here is what I get from Chrome Dev Tools:
index.js?a960:1 Uncaught TypeError: Cannot set property '$GMaps' of undefined
at e.default (index.js?a960:1)
at Object.use (runtime-core.esm-bundler.js?5c40:2949)
at eval (main.js?56d7:14)
at Module../src/main.js (app.js:2254)
at __webpack_require__ (app.js:849)
at fn (app.js:151)
at Object.1 (app.js:2315)
at __webpack_require__ (app.js:849)
at checkDeferredModules (app.js:46)
at app.js:925
e.default @ index.js?a960:1
use @ runtime-core.esm-bundler.js?5c40:2949
eval @ main.js?56d7:14
./src/main.js @ app.js:2254
__webpack_require__ @ app.js:849
fn @ app.js:151
1 @ app.js:2315
__webpack_require__ @ app.js:849
checkDeferredModules @ app.js:46
(anonymous) @ app.js:925
(anonymous) @ app.js:928
I have no idea how to debug this, any help is much appreciated!
Hello,
First thank you for the great package, really you have to add it here : https://github.com/vuejs/awesome-vue#map ;)
I want to create a map and I put all user from my DB into the map, when I click on the user profile I see the information.
I am new to both vue and nuxt. It took quiet a bit of time to figure out how to make this plugin work in Nuxt. I am using Nuxt js 2.14.
I've had to create a plugin and mark it as { src: '~/plugins/x5-maps.js', ssr: false }
to make the plugin work. After all the setup, and trying to capture the "move" event for the marker, the even is never firing.
<template>
<div id="app">
<gmaps-map @mounted="ready">
<gmaps-marker :position="markerPos" :options="markerOptions" @positionChanged="updatePosition" />
</gmaps-map>
<input id="autocomplete" ref="autocomplete" placeholder="Search">
</div>
</template>
<script>
import { gmapsMap, gmapsMarker } from 'x5-gmaps'
export default {
components: { gmapsMap, gmapsMarker },
data: () => ({
autocomplete: null,
places: null,
map: null,
marker: null,
markerPos: { lat: -27, lng: 153 },
markerOptions: {
draggable: true,
clickable: true
}
}),
methods: {
ready (map) {
this.map = map
this.$GMaps().then((maps) => {
this.places = new maps.places.PlacesService(map)
this.autocomplete = new maps.places.Autocomplete(this.$refs.autocomplete)
this.autocomplete.addListener('place_changed', this.update)
})
},
updatePosition (pos) {
console.log('Position changed to:', pos)
this.markerPos = pos
},
addMarker ($event) {
// Stop last marker from bouncing
if (this.markers.length) { this.markers[this.markers.length - 1].animation = null }
// Add new marker with a bounce
this.pos = pos.head
const position = $event.latLng.toJSON()
this.markers.push({ position, animation: this.bounce })
},
update () {
const place = this.autocomplete.getPlace()
if (place.geometry) {
this.map.panTo(place.geometry.location)
this.markerPos = place.geometry.location
}
}
}
}
</script>
<style lang="css" scoped>
/* ... */
#app {
height: 100%;
width: 100%;
}
* >>> #autocomplete {
border: 3px solid rgba(0, 60, 255, 0.329);
font-size: 18px;
height: 40px;
left: 40%;
padding: 0 10px;
position: absolute;
top: 0;
width: 20%;
z-index: 1;
}
</style>
Here, the mounted event of the gmaps-map
is firing properly, but the "move" event of gmaps-marker
is not firing.
Please help me understand if I've to do anything specific to use this component library in NUXT JS
Google Maps Autocomplete input field inside Bootstrap modal dialog and the autocomplete result is not displayed. However, if I press the down arrow key, it selects the next autocomplete result, so it seems that the autocomplete code works correctly, only that the result is not displayed correctly. Maybe it's hidden behind the modal dialog?
i tried
.pac-container {
z-index: 10000 !important;
}
Now I'm struggling to make autocomplete working inside 2nd layer Boostrap Modal. Unfortunately, the z-index trick doesn't work here.
Hello,
I want to use Marker Cluster and when the user click on a marker I want to show a popup with informations
Currently my code like that:
<gmaps-map :options="mapOptions">
<gmaps-cluster :items="drivers" height="200px"> </gmaps-cluster>
</gmaps-map>
and the popup:
<gmaps-map :options="mapOptions">
<gmaps-popup
v-for="(driver, index) in drivers"
:key="index"
:position="driver.position"
height="200px"
:background="driver.organ_transfer ? 'red' : '#ddd'"
>
<div class="text-center">
<a
class="d-block"
:class="{
'text-success': !driver.organ_transfer,
'text-white': driver.organ_transfer,
}"
href="#"
@click.prevent="driver.full_details = true"
>{{ driver.name }}</a
>
<div
v-if="driver.full_details"
:class="{
'text-success': !driver.organ_transfer,
'text-white': driver.organ_transfer,
}"
>
<img :src="driver.avatar" width="48" class="img-thumbnail rounded-circle" />
<div>{{ driver.email }}</div>
<div>{{ driver.phone }}</div>
<a href="" @click.prevent="driver.full_details = false">Close</a>
</div>
</div>
</gmaps-popup>
</gmaps-map>
Hello, thanks for all of your work on the libary. It's great to use.
I wanted to ask if there is a way to alter the map center, after it's initialization. As the gmaps options are currently only avaible through an object, you can't change values on the fly through named props like you can do with gmarkers. Is there a workaround for that?
I think this event binding should only refresh on idle
not bounds_changed
. Bounds changed gets call a lot while dragging
Sometimes it might be useful to access the map instance itself as you do in your children through provide/inject. Right now, I created workaround - custom component that is child of map instance which emits the injected this.getMap()
to the parent.
hidden.vue
<template>
<div style="display: none"></div>
</template>
<script>
export default {
name: "Hidden",
inject: ["getMap", "handleError"],
emits: ["mounted"],
mounted() {
const map = this.getMap();
this.$emit("mounted", map);
},
};
</script>
parent.vue
<template>
<gmaps-map>
<maps-hidden @mounted="mapInstance = $event" />
</gmaps-map>
</template>
<script>
export default {
data() {
return {
mapInstance: null,
};
}
}
</script>
( Error in callback for watcher "options": "TypeError: Cannot read property 'setOptions' of undefined"
Cannot read property 'setOptions' of undefined )
Code =
<gmaps-info-window v-if="canOpenInfoWindo" @closed="hideInfoWindow()"
:options="{position:{lat:infoLat, lng:infoLng},content:info,minWidth:250,pixelOffset:{width:0,height:-38}}" :key="infoLat+infoLng"/>
Same bug as #77 but for generic Markers.
See https://github.com/xon52/x5-gmaps/blob/master/src/components/Marker.ts#L14
Should be MarkerLabel interface not Icon.
See https://github.com/xon52/x5-gmaps/blob/master/src/components/Marker.ts#L26
Not sure why toString is called on it, I think it should be passed through untouched.
Event circle_changed
is fired too many times (I guess for every pixel) in case I drag the circle not exactly in the center of the circle. When I drag the little dot, it's fired just once.
It would be interesting to have a way to have an info window for each marker. It could even be a single instance of the information window, but when clicking on the marker, it would open an information window with the content destined for the clicked marker.
Hey, I wanted to use places plugin, but had no idea how to use it. It could save some time if it were mentioned in the docs. I will submit a PR if you have no time.
The Marker component looks like it should handle MarkerOptions however when the markers are created by the Cluster component, options isn't bound on the Marker.
All of this is so I can set optimized: false
on my clustered pins. Making that a prop on Marker
would be a bonus, but I don't think is necessary at the moment.
When running as SSR you get this undefined window error and a 500 Internal Error on the server.
Project is created via vue-cli
error during render : /
ReferenceError: window is not defined
at loaded ( /node_modules/x5-gmaps/dist/x5-gmaps.ssr.js:299:3)
at init ( /node_modules/x5-gmaps/dist/x5-gmaps.ssr.js:322:7)
at Function.installX5Gmaps [as install] ( /node_modules/x5-gmaps/dist/x5-gmaps.ssr.js:3101:36)
at Function.Vue.use ( /node_modules/vue/dist/vue.runtime.common.dev.js:5087:22)
at Module.a576 (src/main.js:100:14)
at a (webpack/bootstrap:36:26)
at module.exports.0463 (src/components/layout/navigation/MenuDesktop.vue?b699:1:0)
at Object.<anonymous> (src/components/layout/navigation/MenuDesktop.vue?b699:1:0)
at evaluateModule ( /node_modules/vue-server-renderer/build.dev.js:9322:21)
at /node_modules/vue-server-renderer/build.dev.js:9380:18
Implementation:
Parent/Wrapper Component
<template>
<div class="c3-google-maps" :style="styles">
<gmaps-map :options="mapOptions">
<gmaps-marker :options="markerOptions" />
</gmaps-map>
</div>
</template>
<script>
import { gmapsMap, gmapsMarker } from 'x5-gmaps';
export default {
name: 'GMaps',
components: {
gmapsMap,
gmapsMarker,
},
props: {
width: {
type: String,
required: false,
default: '500px',
},
height: {
type: String,
required: false,
default: '500px',
},
mapOptions: {
type: Object,
required: true,
},
markerOptions: {
type: Object,
required: true,
},
},
computed: {
styles() {
return {
width: this.width,
height: this.height,
};
},
},
};
</script>
<style scoped lang="scss">
.c3-google-maps {
width: 100%;
height: 500px;
}
</style>
Parent:
<div>
<g-maps
width="700px"
height="500px"
:marker-options="{ position: { lat: 47.2983606, lng: 10.9810179 } }"
:map-options="{ center: { lat: 47.2988888, lng: 10.9800412 }, zoom: 15, mapTypeId: 'roadmap', }"
/>
</div>
I posted this question on Stack Overflow:
https://stackoverflow.com/questions/69380168/google-places-autocomplete-api-abbreviate-cardinal-direction-on-input
Followed the autocomplete tutorial and created a custom vue combobox that could handle google autocomplete predictions. However, the user has to type in eg "North" fully when searching for an address with a cardinal direction, instead of eg "n". If typing in eg "n" and then pressing space, no results are found.
Is there a way to configure the autocomplete search to accept abbreviated cardinal directions? Is this about adding parameters to the getPlacePredictions() query?
Sorry this isn't EXACTLY related to X5-gmaps. However, I am using this module as a basis for the autocomplete service
For me as a TypeScript user the support would be very helpful.
At the moment I get the following error messages.
Inside VS Code:
Could not find a declaration file for module 'x5-gmaps'. '…/node_modules/x5-gmaps/dist/index.js'
implicitly has an 'any' type. Try `npm install @types/x5-gmaps` if it exists or add a new
declaration (.d.ts) file containing `declare module 'x5-gmaps';
On trying to use:
ReferenceError: window is not defined
at Object.<anonymous> (…/node_modules/x5-gmaps/dist/index.js:1:204)
…
When the computed is changed second time when markers are rendered already, some errors are thrown. I guess it's because the old markers are not destroyed but don't have the values anymore (?).
<gmaps-marker
v-for="(marker, markerIndex) in markers"
:key="'marker-' + markerIndex"
:position="marker.position"
@click="showMarker(marker)"
@closed="markerDetail.show = false"
/>
[Vue warn]: Error in callback for watcher "_options": "TypeError: can't access property "lat", t is undefined"
Thank you for this lib, it was sheer joy removing my horrible custom vue-gmaps implementation and using yours. I'm on v0.5.6 and was hoping to be able to use the MarkerLabel interface on my cluster elements.
However, according to this line...
The relevant property label
is mapped to google.maps.Icon
not google.maps.MarkerLabel
, looks like maybe this got overlooked in a merge?
Hi,
would it be possible to influence map markers through cluster items? I mean, cluster prop items would be an array of marker props. I'm mainly interested in options and icons.
Thanks
Pretty self explanatory. I'll provide the PR. Do you want to split it to another repo, create monorepo or have branches like main containing vue3 code and vue2 branch containing vue2 code? Or main could have vue2 and next vue3 till the shift in npm vue package when vue will become vue3 by default.
As per PR#36
Currently the maps component supports the following events
@bounds-changed returns new bounds
@center-changed returns new center
@zoom-changed returns new zoom level
@click returns event
@double-click returns event
@right-click returns event
I propose adding @idle
as another since it auto-debounces bounds-changed
for you.
Marker icon should be either string or object (define different size).
"x5-gmaps": "0.5.2" has no problem with the dependency.
I'm trying to use cluster component that is not documented here https://xon52.github.io/x5-gmaps/api/ but I found it here demo https://codesandbox.io/s/x5-gmaps-demo-6xww1. But it fails on the error below.
<template>
<gmaps-map v-bind:options="mapOptions">
<gmaps-cluster
v-bind:items="mapMarkers"
v-bind:options="clusterOptions"
/>
</gmaps-map>
</template>
<script>
mapOptions: {
center: { lat, lng },
zoom: 5,
fullscreenControl: false,
mapTypeControl: false,
rotateControl: false,
scaleControl: false,
streetViewControl: false,
zoomControl: true,
},
clusterOptions: {
minZoom: 1,
maxZoom: 7,
highPercentage: 10,
lowPercentage: 3,
},
mapMarkers: [], // array of objects {id, lat, lng, title}
</script>
x5-gmaps.esm.js:1635 Uncaught TypeError: Cannot read property 'contains' of undefined
at VueComponent.shouldFilter (x5-gmaps.esm.js:1635)
at VueComponent.refresh (x5-gmaps.esm.js:1654)
at ij.<anonymous> (x5-gmaps.esm.js:1699)
Greetings!
Thank you for your great map component.
I've read 'README' and the issues with 'thefoxie' ,
but I am poor at Vue, I could not access the google map instance.
Could you help me to understand how to use it ?
I'd like to use Google map methods(ex:panTo) after it's mounted with some click function.
I've tried this,
import { gmapsMap, gmapsMarker, gmaps } from "x5-gmaps";
export default {
components: { gmapsMap, gmapsMarker },
data: () => ({
mapInstance: null,
methods: {
someClickFuntion() {
this.mapInstance.panTo({lat:0,lng:0});
},
mounted() {
gmaps().then((GMaps) => {
this.mapInstance= GMaps;
});
},
Error :TypeError: this.mapInstance.panTo is not a function
It's not mentioned in docs but the project where it's used won't build without it. I no longer use components but I keep the library to load google libs for me which is valid use case I guess(?). I use leaflet now with google maps tiling and autocomplete.
Apart from that, I had lots of fun with this plugin but there's no official support for spiderify in google's map clusterer which is real shame as it's something I have to use ( around 1000 markers and some of them got same lng/lat).
"x5-gmaps": "0.4.5"
has no problem with the dependency.
Hello,
There is a way to move/marker and get the lat/lng ?
Thanks
I am trying to set the origin
for marker, but I do not know how to access maps.Point
. Any ideas?
icon: {
url: '.../myimage.png',
scaledSize: new maps.Size(60, 30),
origin: new maps.Point(30, 30),
},
Thanks a lot for this project!
I'm trying to run x5-gmaps in a Quasar app:
import { gmapsMap, gmapsMarker } from 'x5-gmaps'
export default {
components: { gmapsMap, gmapsMarker },
}
<template>
<gmaps-map>
<gmaps-marker :position="{ lat: -27, lng: 153 }" />
</gmaps-map>
</template>
import Vue from 'vue'
Vue.use(gmapsMap, { key: 'XXXXXXX, libraries: ['places'] })
But I get this error:
Error in mounted hook: "TypeError: this.$GMaps is not a function"
I'm already tried to create this.$GMaps
manually putting the following code in a quasar boot
file:
//file /src/boot/gmaps.js
import { gmapsMap } from 'x5-gmaps'
export default ({ app, router, Vue }) => {
Vue.prototype.$GMaps = gmapsMap
Vue.use(gmapsMap, { key: 'XXXXX', libraries: ['places'] })
}
But the same error occurs. Any idea? Thanks in advance!
hi,
i install this npm package and import it in vue component. no error, but the maps doesn't show up, it's blank page for map area. do i need to include maps.js from google to laravel head script or other reasons? thanks.
As we discussed in the other issue, there's need for granularity option for events. I guess there's use case for setting this in every event. For instance, I would love to have instant response when I resize circle. It's what we're used to - reactivity that comes with vue. In circle, this could be implemented as step-size="1000" (in meters), which would also change step of drag/circle expansion.
First of all, thank you for creating this amazing plugin for Vue.
I would like to ask if you have any plans to add support for drawing polylines on the map. A use case will be for drawing the path a certain marker took (from a start position to end position), for example in JavaScript:
// This example creates a 2-pixel-wide red polyline showing the path of
// the first trans-Pacific flight between Oakland, CA, and Brisbane,
// Australia which was made by Charles Kingsford Smith.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 3,
center: {lat: 0, lng: -180},
mapTypeId: 'terrain'
});
var flightPlanCoordinates = [
{lat: 37.772, lng: -122.214},
{lat: 21.291, lng: -157.821},
{lat: -18.142, lng: 178.431},
{lat: -27.467, lng: 153.027}
];
var flightPath = new google.maps.Polyline({
path: flightPlanCoordinates,
geodesic: true,
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2
});
flightPath.setMap(map);
}
Which renders:
Would be nice to be able to support multiple polylines with options to set the color, opacity, line weight, etc.
Edit: Ref https://developers.google.com/maps/documentation/javascript/examples/polyline-simple
Hope you can consider.
Thanks in advance!
Great plugin.
Would be nice to have the Address auto complete as well as the map.
great work. regards.
Hello,
There is anyway I can detect the drag-end event in gmap-maker, the move event is good but it's too sensitive, I only need the lat lng when the drag is stop and that all
I'm able to access the map instance, but I feel like I'm doing something else wrong ( markers load correctly, popup window works great ect ):
Mounted:
this.$GMaps().then((GMaps) => {
this.mapInstance = GMaps;
}).then(() => {
this.getLocationService();
});
And when I reference it again for my API call, I get fitbounds is not a function:
Service.getLocations(this.formData).then((response) => {
const results = response.data;
// eslint-disable-next-line no-undef
this.bounds = new google.maps.LatLngBounds();
this.markers = results;
results.forEach((data) => {
const marker = data.position;
// eslint-disable-next-line no-undef
const position = new google.maps.LatLng(marker);
this.bounds.extend(position);
});
})
.then(() => {
const map = this.mapInstance;
console.log(map);
map.fitBounds(this.bounds);
})
.catch((error) => {
// eslint-disable-next-line no-console
console.log(error);
});
I feel like I'm missing something, or should fitBounds
be applied different in this plugin?
Hi is there a option to leave the InfowWindow Closed on the beginning?
Thx for your help
<gmaps-info-window ref="b" :options="optionsWindow" >
<ImmoObjectMap v-bind:key="windowcontent.we" :immo="windowcontent" />
</gmaps-info-window>
i tried to close it when mounted but it is not working
this.$refs.b.open()
I find the x5-gmaps now does have all the functions i need except the mark clustering (https://developers.google.com/maps/documentation/javascript/marker-clustering), as the markers number is getting bigger and unmanageable soon. thanks.
This may not be directly relevant to this repo, But I'm using your maps and they are really nice.
However I noticed when they load they have a grey background and a white circle spinner.
Is there anyway to customize this ?
After testing the latest version of this in a mockup Typescript project, I have found that inside a Vue component
I cannot seem to avoid the eslint error: 'google' is not defined.eslint (no-undef)
.
It seems to work fine inside a .ts
file, and all the Google API Types are available (e.g. google.maps.LatLngLiteral
), but that error persists.
Would love some help in finding what's needed here. It may even be an eslint problem, but not sure.
It should be possible to create component with geojson instead of google objects. I do create polygons like this as I receive large (sometimes 70k points) sets of data that I work with and it would be preferred to not process the data just for component again . I've added this to my app, but not as a component. I will look into this later and send PR.
Can you provide us a small demo code where we can use this plugin so we can display user's current location.
Hello there, I'm having a task where I need to update user marker position.
I checked the example code provided where you use the @Move event for marker.
My sample code looks something like this.
<gmaps-map :options="mapOptions"> <gmaps-marker :options="shopOptions" @move="updatePosition" /> </gmaps-map>
updatePosition(pos) { this.shopOptions.position = pos },
The code updates the position, however the move event fires too many times and makes the app laggy also trows this vue warning
vue.runtime.esm.js?2b0e:619 [Vue warn]: You may have an infinite update loop in watcher with expression "_options"
found in
---> <GmapsMarker> at src/components/Marker.vue
<GmapsMap> at src/components/Map.vue
Is there some way to get the position once when the @Move event finish ?
Or maybe to get the new position when you click on some "save button".
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.