今日はAngularで複数の地図を表示する方法を紹介します。
(とはいえこの方法が必要となるシチュエーションはあまりないのかなとは思います笑。が、必要となった時参考にしてください)
【前提】地図を表示するのに使うもの
まず、地図を表示するにはleafletというOpenMapStreetのAngularライブラリを使います。
leafletの使い方は以下を参考にしてください。
https://qiita.com/uedayou/items/30f88d88238f648e8ee6
ここから先はleafletは使える前提で話を進めます。
複数の地図を表示する
ページに一つだけ地図を表示する場合は何も問題ありません。
上記のURLを参考に地図を表示できます。
問題は地図を複数表示する場合です。
複数の地図を表示する場合以下2つのパターンが考えられます。
- 静的表示:html文に<div id=”map”></map>を地図の数分追加する
- 動的表示:地図を表示するコンポーネントを別で定義し、コンポーネントを地図の数分追加する
1は特に問題がないのですが、2番は工夫が必要になります。
そもそも何が問題になるかというと、leafletの以下の文
this.map = L.map('map').setView([34.702485,135.495951], 13);
のmap関数の引数「’map’」はhtml要素のうち「map」というidの要素を指す点です。
map関数はHTMLElement型の引数も受け取れますが、
HTMLElement型の要素は、javascript標準のdocument.getElememntById関数でしか取得できません。
つまり、「html要素のとあるid」と「地図オブジェクト」を結びつけているわけです。
idは一意なので、複数の地図を表示するにはidを地図分用意しなくてはいけません。
1の静的表示について考えてみましょう。
htmlに直接ダグを増やせる場合は問題ないですね。
<div id="map1"></map>
<div id="map2"></map>
<div id="map3"></map>
<div id="map4"></map>
<div id="map5"></map>
<div id="map6"></map>
・・・
上記のようにidを被らないようにするだけです。
map関数も地図の数分実行できます。
問題が起きるケースとは。。。
2の動的表示の場合はどうでしょう。
以下のような流れを考えてみます。
地図を表示するコンポーネントは別で定義したい。
↓
地図を表示するコンポーネント:map.component.ts
上記コンポーネントを追加するコンポーネント:app.component.ts
↓
表示したい地図の数分app.compoennt.htmlにセレクターを追加すると表示できるはず?
import { Component, OnInit } from '@angular/core';
import * as L from 'leaflet';
@Component({
selector: 'map-component',
template: '<div class="map" id="map"></div>'
})
export class MapComponent implements OnInit {
map: any;
ngOnInit(){
this.map = L.map('map').setView([34.702485,135.495951], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map);
}
}
<div class="container">
<map-component></map-component>
<map-component></map-component>
<map-component></map-component>
<map-component></map-component>
<map-component></map-component>
<map-component></map-component>
</div>
実行してみます。
左上の一つしか表示されていません。
これは、6つのdiv要素に同じmapというidが付けられ、6つのマップがmapというidに紐づけられているからです。
idは一意なので左上のdiv要素でidが引っかかり、他のdiv要素にマップが結びつけられなかったためだと思われます。
解決方法
map.component.tsのクラス変数(静的プロパティ)を利用する方法を紹介します。
map.component.tsに以下の赤文字部分を追加します。
map.component.ts
import { Component, OnInit } from '@angular/core';
import * as L from 'leaflet';
@Component({
selector: 'map-component',
template: '<div class="map" id="map" ・・・①></div>'
})
export class MapComponent implements OnInit {
static number: number = 0; ・・・②
map: any;
ngOnInit(){
MapComponent.number++; ・・・③
document.getElementsByClassName('map')[MapComponent.number-1].id = 'map' + MapComponent.number; ・・・④
this.map = L.map('map' + MapComponent.number ・・・⑤).setView([34.702485,135.495951], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map);
}
}
①id=”map”削除
idに「map+識別番号」を割り振るので削除しておきます。
②static number: number = 0 の追加
静的プロパティを定義します。これでMapComponentのインスタンス作成時に初期化されないプロパティを使用できます。
最初は0を入れておきます。
③MapComponent.number++; の追加
ngOnInitが実行されるたび(MapComponentインスタンスが生成されるたび)に静的プロパティをインクリメント(+1すること)します。
④document.getElementsByClassName(‘map’)[MapComponent.number-1].id = ‘map’ + MapComponent.number; の追加
document.getElementsByClassName(‘map’)でmapというクラスのhtml要素を全て取得します。ここで所得される要素はElement型の配列です。Element要素はmap関数にそのままぶち込むことはできません。
取得した配列の「MapComponent.number-1」の要素のidに「map○」を設定します。(○は1から始まる整数)
①から④のコードで、
<div id="map1"></map>
<div id="map2"></map>
<div id="map3"></map>
<div id="map4"></map>
<div id="map5"></map>
<div id="map6"></map>
のようなidの割り振りになります。
⑤‘map’ + MapComponent.number
「map○」を指定してマップ関数を実行します。
実行結果は以下です。
6つとも地図が表示されました!(安心)
まとめ
今回複数の地図を表示する方法を紹介しました。
idを重複しないようにするというだけですが、原因がわからないと時間を食ってしまったところでもあります。
みなさんはこちら参考に好きなだけ地図を表示してもらいたいです。
ちなみに、ViewContainerRefなどで動的にMap.component.tsを追加する場合も今回のような方法で大丈夫です。
Thanks for the good article, I hope you continue to work as well.