问题
Here's my problem - a Vue2 leaflet map does not render correctly in BootstrapVue modal.
Here's what it looks like visually (it should show just the ocean)
<template>
<div>
<b-modal size="lg" :visible="visible" @hidden="$emit('clear')" title="Event details">
<div class="foobar1">
<l-map :center="center" :zoom="13" ref="mymap">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<template slot="modal-footer">
<b-btn variant="danger" @click="deleteEventLocal(event.id)">Delete</b-btn>
</template>
</b-modal>
</div>
</template>
<script>
import * as moment from "moment";
import { LMap, LMarker, LTileLayer } from "vue2-leaflet";
import { deleteEvent } from "./api";
import "vue-weather-widget/dist/css/vue-weather-widget.css";
import VueWeatherWidget from "vue-weather-widget";
export default {
data() {
return {
center: L.latLng(event.latitude, event.longitude),
url: "http://{s}.tile.osm.org/{z}/{x}/{y}.png",
attribution:
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
};
},
props: {
visible: {
type: Boolean
},
event: {
required: true,
type: Object
}
},
methods: {
async deleteEventLocal(id) {
await deleteEvent(id);
this.$emit("refresh");
this.$emit("clear");
}
},
components: {
weather: VueWeatherWidget,
LMap,
LMarker,
LTileLayer
}
};
</script>
As you can see there aren't any CSS rules that could make the map spill outside the modal as it does. Which is weird.
I'm kinda asking this question to answer it myself as I couldn't find a solution before.
回答1:
There were 3 issues because of which this was happening.
- First - I forgot to load the leaflet css into
main.js
- this is why the leaflet map was somehow outside the modal.
//main.js
import '@babel/polyfill';
import Vue from 'vue';
import './plugins/bootstrap-vue';
import App from './App.vue';
import router from './router';
import store from './store';
//above imports not important to this answer
import 'leaflet/dist/leaflet.css'; //<--------------add this line
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
- Now the map may disappear. Set a width and height on the
l-map
component's container. I used a class but you can use style="" etc.
<div class="foobar1"> <!-- <--- Add a class on l-map's container -->
<l-map :center="center" :zoom="13">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<style lang="scss">
.foobar1 {
width: 100%;
height: 400px;
}
</style>
- Now your map will render within the modal but if you move the map's view, you'll see that leaflet does not download the maps' squares in time. You will see something like this:
To fix this, create an event handler on b-modal
for the @shown
event.
<b-modal
@shown="modalShown"
@hidden="$emit('clear')"
size="lg"
:visible="visible"
title="Event details"
>
I called mine modalShown
.
Then, add a ref
attribute to your l-map
. I called mine mymap
.
<l-map ref="mymap" :center="center" :zoom="13">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
Then create a modalShown
method in the Vue methods for your view/component and call invalidateSize()
inside. I don't know why it works. invalidateSize
is part of native leaflet API (vue2leaflet is just a wrapper), and we're calling it in this method.
export default {
data() {
//some data here
}
methods: {
modalShown() {
setTimeout(() => {
//mapObject is a property that is part of leaflet
this.$refs.mymap.mapObject.invalidateSize();
}, 100);
}
}
}
Now everything should be fine - the map should not spill outside the modal, the map should be visible (duh) and the map squares should be downloaded when they are within the map body.
Here's my full code, it contains some stuff specific to my app but overall it contains all of the code snippets above.
来源:https://stackoverflow.com/questions/56364129/vue2-leaflet-map-not-showing-properly-in-boostrapvue-modal