概要

BootstrapVue のモーダル内で VueLeaflet を使うと、表示崩れが起きてしまいます。
以下では普通に実装したときの失敗例と、そうならないよう対策した成功例を載せています。

準備

まずは Vue プロジェクトの作成と必要なライブラリをインストールします。
今回使用したバージョンは以下の通りです。

Vue 2.6.11
Vue2Leaflet 2.5.2
BootstrapVue 2.10.1

vue create myproject --default && cd myproject
npm install bootstrap-vue
npm install leaflet vue2-leaflet

main.js を編集してライブラリを読み込ませます。

import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

// BootstrapVue
import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue";

import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue/dist/bootstrap-vue.css";

Vue.use(BootstrapVue);
Vue.use(BootstrapVueIcons);

// Vue2Leaflet
import "leaflet/dist/leaflet.css";

new Vue({
  render: h => h(App)
}).$mount("#app");

App.vue を編集します。

<template>
  <div>
    <b-button v-b-modal.modal-1>Launch demo modal</b-button>

    <b-modal id="modal-1" title="BootstrapVue">
      <p class="my-4">Hello from modal!</p>
    </b-modal>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

以上で準備ができました。
次からは <b-modal> タグ内に Leaflet での地図表示を実装していきます。

失敗例

先に失敗例から見ていきます。
App.vue を開いて以下のように編集します。

<template>
  <div>
    <b-button v-b-modal.modal-1>Launch demo modal</b-button>

    <b-modal id="modal-1" title="BootstrapVue">
      <div id="app">
        <l-map ref="map" style="height: 300px;" :zoom="zoom" :center="center">
          <l-tile-layer :url="url"></l-tile-layer>
        </l-map>
      </div>
    </b-modal>
  </div>
</template>

<script>
import { LMap, LTileLayer } from "vue2-leaflet";

export default {
  name: "App",
  components: { LMap, LTileLayer },
  data() {
    return {
      url: "http://{s}.tile.osm.org/{z}/{x}/{y}.png",
      zoom: 8,
      center: [35.693825, 139.703356]
    };
  }
};
</script>

ローカルサーバーを起動して開いてみるとわかりますが、地図がうまく表示されません。

成功例

次に解決策を示します。

表示が崩れている原因はモーダルのサイズと地図のサイズがあっていないことにあります。
よってモーダルが開いた時に地図のサイズを変更するように修正します。

App.vuemethods プロパティを追加します。
init() 関数では Leaflet API が提供しているサイズ変更の関数を呼び出しています。

setTimeout で実行時間を遅らせているのはモーダルを開いた直後だと地図の描写処理が終わっておらず、サイズが確定できないためです。

export default {
  //...略
  methods: {
    init() {
      setTimeout(() => {
        this.$refs.map.mapObject.invalidateSize();
      }, 10);
    }
  }
};

あとはこの関数をイベントリスナに登録します。

<l-map ref="map" style="height: 300px;" :zoom="zoom" :center="center">
  <l-tile-layer :url="url" @ready="init"></l-tile-layer>
</l-map>

以上で、表示が崩れることなくモーダル内で地図が描写されるようになっています。