Google Maps showing grey box in Vue modal - vue.js

I have a <b-modal> from VueBootstrap, inside of which I'm trying to render a <GmapMap> (https://www.npmjs.com/package/gmap-vue)
It's rendering a grey box inside the modal, but outside the modal it renders the map just fine.
All the searching I've done leads to the same solution which I'm finding in some places is google.maps.event.trigger(map, 'resize') which is not working. Apparently, it's no longer part of the API [Source: https://stackoverflow.com/questions/13059034/how-to-use-google-maps-event-triggermap-resize]
<template>
<div class="text-center">
<h1>{{ title }}</h1>
<div class="row d-flex justify-content-center">
<div class="col-md-8">
<GmapMap
ref="topMapRef"
class="gmap"
:center="{ lat: 42, lng: 42 }"
:zoom="7"
map-type-id="terrain"
/>
<b-table
bordered
dark
fixed
hover
show-empty
striped
:busy.sync="isBusy"
:items="items"
:fields="fields"
>
<template v-slot:cell(actions)="row">
<b-button
size="sm"
#click="info(row.item, row.index, $event.target)"
>
Map
</b-button>
</template>
</b-table>
<b-modal
:id="mapModal.id"
:title="mapModal.title"
#hide="resetInfoModal"
ok-only
>
<GmapMap
ref="modalMapRef"
class="gmap"
:center="{ lat: 42, lng: 42 }"
:zoom="7"
map-type-id="terrain"
/>
</b-modal>
</div>
</div>
</div>
</template>
<script>
// import axios from "axios";
import { gmapApi } from 'gmap-vue';
export default {
name: "RenderList",
props: {
title: String,
},
computed: {
google() {
return gmapApi();
},
},
updated() {
console.log(this.$refs.modalMapRef);
console.log(window.google.maps);
this.$refs.modalMapRef.$mapPromise.then((map) => {
map.setCenter(new window.google.maps.LatLng(54, -2));
map.setZoom(2);
window.google.maps.event.trigger(map, 'resize');
})
},
data: function () {
return {
items: [
{ id: 1, lat: 42, long: 42 },
{ id: 2, lat: 42, long: 42 },
{ id: 3, lat: 42, long: 42 },
],
isBusy: false,
fields: [
{
key: "id",
sortable: true,
class: "text-left",
},
{
key: "text",
sortable: true,
class: "text-left",
},
"lat",
"long",
{
key: "actions",
label: "Actions"
}
],
mapModal: {
id: "map-modal",
title: "",
item: ""
}
}
},
methods: {
// dataProvider() {
// this.isBusy = true;
// let promise = axios.get(process.env.VUE_APP_LIST_DATA_SERVICE);
// return promise.then((response) => {
// this.isBusy = false
// return response.data;
// }).catch(error => {
// this.isBusy = false;
// console.log(error);
// return [];
// })
// },
info(item, index, button) {
this.mapModal.title = `Label: ${item.id}`;
this.mapModal.item = item;
this.$root.$emit("bv::show::modal", this.mapModal.id, button);
},
resetInfoModal() {
this.mapModal.title = "";
this.mapModal.content = "";
},
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
margin-bottom: 60px;
}
.gmap {
width: 100%;
height: 300px;
margin-bottom: 60px;
}
</style>
Does anyone know how to get the map to display properly in the modal?
Surely, I'm not the first to try this?

Had this problem, in my case it was solved by providing the following options to google maps:
mapOptions: {
center: { lat: 10.365365, lng: -66.96667 },
clickableIcons: false,
streetViewControl: false,
panControlOptions: false,
gestureHandling: 'cooperative',
mapTypeControl: false,
zoomControlOptions: {
style: 'SMALL'
},
zoom: 14
}
However you can probably make-do with just center and zoom.
Edit: Try using your own google maps components, follow this tutorial:
https://v2.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html#Base-Example
You can use the package described in the tutorial to load the map, dont be scared by the big red "deprecated" warning on the npm package page.
However for production, you should use the package referenced by the author, which is the one backed by google:
https://googlemaps.github.io/js-api-loader/index.html
The only big difference between the two:
The 'google' object is not returned by the non-deprecated loader, it is instead attached to the window. See my answer here for clarification:
'google' is not defined Using Google Maps JavaScript API Loader
Happy coding!

Related

How to fix this error: "v-model cannot be used on a prop, because local prop bindings are not writable?"

I'm trying to make a dropdown sort and I get this error:
VueCompilerError: v-model cannot be used on a prop, because local prop bindings are not writable. Use a v-bind binding combined with a v-on listener that emits update:x event instead.
Here are 2 components App and MySelect:
<template>
<!-- App Component -->
<div class="app">
<h1>Страница с постами</h1>
<div class="app__btns">
<my-button #click="showDialog">Cоздать пост</my-button>
<my-select v-model="selectedSort" :options="sortOptions" />
</div>
<my-dialog v-model:show="dialogVisible">
<post-form #create="createPost" />
</my-dialog>
<post-list :posts="posts" #remove="removePost" v-if="!isPostsLoading" />
<div v-else>Идет загрузка...</div>
</div>
</template>
<script>
import axios from 'axios'
import PostForm from './components/PostForm.vue'
import PostList from './components/PostList.vue'
export default {
components: { PostList, PostForm },
data() {
return {
posts: [],
dialogVisible: false,
isPostsLoading: false,
selectedSort: '',
sortOptions: [
{ value: 'title', name: 'По названию' },
{ value: 'body', name: 'По содержанию' },
],
}
},
methods: {
createPost(post) {
this.posts.push(post)
this.dialogVisible = false
},
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id)
},
showDialog() {
this.dialogVisible = true
},
async fetchPosts() {
try {
this.isPostsLoading = true
const res = await axios.get(
'https://jsonplaceholder.typicode.com/posts?_limit=10'
)
this.posts = res.data
} catch (error) {
alert('ошибка')
} finally {
this.isPostsLoading = false
}
},
},
mounted() {
this.fetchPosts()
},
}
</script>
<!-- флаг scoped - значит, что стили будут применяться только к этому комопненту -->
<style>
.app {
padding: 20px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app__btns {
margin: 15px 0;
display: flex;
justify-content: space-between;
}
</style>
<template>
<!-- MySelect component -->
<select v-model="modelValue" #change="changeOption">
<option disabled value="">Выберите из списка</option>
<option v-for="option in options" :key="option.value" :value="option.value">
{{ option.name }}
</option>
</select>
</template>
<script>
export default {
name: 'my-select',
props: {
modelValue: {
type: String,
},
options: {
type: Array,
default: () => [],
},
},
methods: {
changeOption(event) {
this.$emit('update:modelValue', event.target.value)
},
},
}
</script>
<style lang="scss" scoped></style>
I need to update modelValue, so I tried to add
:value="modelValue"
instead of
v-model="modelValue"
and it works, but I'm not sure if this is the correct solution.
If anyone else is encountering this issue when updating their vue version. Please note that this error started to appear on version 3.2.45.
For the implementation pattern, as noted on the documentation, props should be considered readonly within the component. Vue did not enforce it enough prior to version 3.2.45.
Documentation with links to good implementation patterns : https://vuejs.org/guide/components/props.html#one-way-data-flow

How to remove active classes from all buttons before making another one active?

I'm new to vue, I still don't understand everything, tell me. I have buttons that I display through v-for, I need to get the active class of only one button when pressed, all the others need to be turned off, tell me, preferably visually, how can I do it better?
I am using the method activeBtn, but this doesn't turn off the active class from the previous buttons
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
<script>
data() {
return {
buttons: [
{
label: "A",
isActive: false,
type: "border-left",
name: "BorderLeftComonent",
},
{
label: "A",
isActive: false,
type: "text-balloon",
name: "TextBalloonComponent"
},
{
label: "A",
isActive: false,
type: "dashed",
name: "DashedComponent"
},
],
};
},
methods: {
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
}
</script>
<template>
<div id="btn-box">
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
#click="component = button.name, activeBtn($event, index)">
<div :class="`btn btn-${button.type}`">{{ button.label }}</div>
</button>
</div>
</template>
Since you only want to get one active button at any one point, it doesn't make sense to manage the active state inside each button.
Instead, you should manage it at group level by storing the currently selected button's id. A button would then be active when its id matches the currently selected id.
Here's an example:
new Vue({
el: '#app',
data: () => ({
buttons: [
{
id: "button-1",
label: "A",
type: "border-left",
name: "BorderLeftComonent"
},
{
id: "button-2",
label: "A",
type: "text-balloon",
name: "TextBalloonComponent"
},
{
id: "button-3",
label: "A",
type: "dashed",
name: "DashedComponent"
}
],
activeButtonId: "button-1"
}),
methods: {
activate(id) {
this.activeButtonId = id;
}
}
})
.on {
background-color: red
}
.off {
background-color: blue
}
.on, .off {
color: white
}
<script src="https://unpkg.com/vue#2/dist/vue.min.js"></script>
<div id="app">
<div>
<button
v-for="{id, type, label} in buttons"
:key="id"
:class="activeButtonId === id ? 'on' : 'off'"
#click="activate(id)"
>
<div :class="`btn btn-${type}`" v-text="label" />
</button>
</div>
</div>

How to fix Cannot read property 'maps' of null" on Quasar Vue Google Maps?

How can I solve this Cannot read property 'maps' of null", when I call initMap to center the map with this line of code,
const map = new this.google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
I already define this.google in compute also, but it doesn't work.
This is what i have done
<template>
<q-layout view="hhh lpr fff" class="main-layout">
<q-page-container>
<GmapMap
id="map"
ref="mapRef"
:center="{lat:-7.250445, lng:112.768845}"
:zoom="13"
map-type-id="terrain"
style="width: 1000px; height: 600px; margin-left:100px"
>
</GmapMap>
<router-view />
</q-page-container>
</q-layout>
</template>
<script>
import * as VueGoogleMaps from 'vue2-google-maps'
export default {
name: 'DefaultLayout',
data () {
return {
mapName: "map",
property: {
lat: 1.28237,
lng: 103.783098
},
}
},
computed: {
google: VueGoogleMaps.gmapApi
},
mounted () {
this.initMap();
},
methods: {
initMap() {
const map = new this.google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
},
},
}
</script>
<style>
</style>
Anyone can help me solve this problem?
Computed property should be a getter function. I think you don't need a computed property here. You can make everything in the initMap method, e.g.
initMap() {
if(VueGoogleMaps) {
new VueGoogleMaps.gmapApi.maps.Map(document.getElementById('map'), {
zoom: 13,
center: this.property
})
}
}

How do I pass custom options into chartjs

I'm trying to be able to add notes to my charts but i'm stuck on how I would pass my individual photon object into my chartOptions variable to be used in the tooltips label function?
<template>
<swiper-slide v-for="(photon,key) in $store.state.photons" :key='key'>
<line-chart width="80vw" :dataset="dataset" :library="chartOptions" class="animated slideInLeft delay-0.01s"
v-else :data="photon.data.rbChannel" ytitle="R/B Channel" :download="true"></line-chart>
<line-chart width="80vw" :dataset="dataset" :library="chartOptions" class="animated slideInRight delay-0.01s"
v-else :data="photon.data.tempF" ytitle="Temperature F" :colors="['#ff0000']" :download="true"></line-chart>
</swiper-slide>
</template>
chartOptions variable
chartOptions: {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
console.log(data.labels[tooltipItem.index])
console.log(tooltipItem)
return data.labels[tooltipItem.index]
}
}
},
height: '400px',
pan: {
enabled: false,
mode: 'xy',
},
zoom: {
enabled: true,
mode: 'x',
},
drag: true,
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
}
}

How to create a map using Here maps api and Vue.js

I want to load a simple map using Vue.js.
I created a component named map to load the basic map but somethings goes wrong and I tried many things but nothings works with me.
On my index.html I put all the javascript api from Here maps.
I am try to use this sample as a start point.
So,anyone has a Ideia what I am duing wrong?
Thanks.
https://developer.here.com/documentation/maps/topics/quick-start.html
<template>
<div>
<div id="mapContainer">
</div>
</div>
</template>
<script>
export default {
name: 'maps',
data: function() {
return {
map: null,
maptypes: null
}
},
mounted () {
this.initMap ();
},
methods: {
initMap() {
// Initialize the platform object:
var platform = new H.service.Platform({
app_id: 'nyKybJs4fZYfMCd7jfsx',
app_code: 'E_xE5837hGk33SL8M6hWIg',
useCIT: true,
useHTTPS: true
});
this.maptypes = platform.createDefaultLayers();
// Instantiate (and display) a map object:
this.map = new H.Map(
document.getElementById('mapContainer'),
this.maptypes.normal.map,
{
zoom: 10,
center: { lng: 13.4, lat: 52.51 }
});
}
}
}
</script>
<template>
<div>
<div style="width: 100%; height: 500px" id="map-container"></div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data: () => ({ map: null }),
mounted () {
// Initialize the platform object:
const platform = new H.service.Platform({
app_id: 'nyKybJs4fZYfMCd7jfsx',
app_code: 'E_xE5837hGk33SL8M6hWIg',
useCIT: true,
useHTTPS: true,
});
const maptypes = platform.createDefaultLayers();
// Instantiate (and display) a map object:
this.map = new H.Map(
this.$el.getElementById('map-container'),
maptypes.normal.map,
{
zoom: 10,
center: { lng: 13.4, lat: 52.51 },
},
);
},
}
</script>
You should provide explicit size of map container. For example:
<template>
<div>
<div style="width: 100%; height: 500px" id="mapContainer"></div>
</div>
</template>