Nuxt3(Vue3)でGoogleMapAPIを利用した住所検索方法について

# 完成イメージ

1. 住所検索
①テキストボックスに検索地を入力
②検索アイコンをクリックした時に住所検索を行う。
![スクリーンショット 2022-12-27 23.36.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286094/14983561-1d28-f9a5-bab7-8568e9fece91.png)


2. マーカーを設置する。
住所検索した結果、該当データがある場合、地図上にマーカーを設置する。

例）USJを検索してみた結果
![スクリーンショット 2022-12-27 23.37.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286094/ac6abf1a-951c-386b-7b7e-1a4c4b51930a.png)

# 利用環境
```
"nuxt": "3.0.0-rc.11",
"vue": "^3.2.39"
```

# 前提条件
GoogleMapをウェブで利用する為に[Maps JavaScript API](https://developers.google.com/maps/documentation/javascript?hl=ja
)を利用します

APIキーの取得方法は[こちらの記事](https://www.javadrive.jp/google-maps-javascript/charset/index1.html)参考ください。

# 導入方法

# loaderをインストール
```
npm i @googlemaps/js-api-loader
```
https://www.npmjs.com/package/@googlemaps/js-api-loader

https://developers.google.com/maps/documentation/javascript/overview?hl=ja#Loading_the_Maps_API

`@googlemaps/js-api-loader`は、`Maps JavaScript API` を動的に読み込み、プロセスを Promise でラップします。 (JavaScript によって Maps JS API を読み込むことが可能となります)

読み込みオプションを利用して、ージョン、ライブラリ、apiKeyなどを指定することが可能になります。[オプション参考](https://googlemaps.github.io/js-api-loader/interfaces/LoaderOptions.html)

# 実装

templateには下記を追加します。

``` vue
<template>
  <div>
    <div id="map" style="width: 100%; height: 600px;"></div>
      <div>
        <input
          class="px-3 py-2 d-table-cell search-form-address"
          v-model="searchText"
          placeholder="Search Address"
          style="display:block;"
        >
      <div>
        <button
          class="inline-flex p-2 hover:bg-gray-100 rounded"
          @click="addressSearch"
        >
        検索
        </button>
      </div>
    </div>
  </div>
</template>

<script>
import { Loader } from '@googlemaps/js-api-loader';

export default {
  data(){
    return {
      map: null,
      google: null,
      marker: null,
	  searchText: null,
    }
  },

  mounted(){
    new Loader({
      // 取得したAPIキーを入力
      apiKey: '********************************',
      // 動作が安定したバージョン。この段階のバージョンは、バグ修正で変更されることはある。
      version: 'Release',
      // 追加のライブラリを読み込む場合はライブラリ（複数可）の名前を指定
      // 参照：https://developers.google.com/maps/documentation/javascript/libraries?hl=ja
      libraries: ["places", "drawing", "geometry", "visualization"],
      language: 'ja',
    })
    .load()
    .then((google) => {
      this.google = google;
	    // 地図の初期化
      this.map = new google.maps.Map(document.getElementById('map'), {
          // コントロールの概要設定
          zoom : 17, // 初期表示設定
          center: { lat: 35.692195, lng: 139.759854 },    // マルティスープ本社
          fullscreenControl: false, // 全画面モードで地図を開くコントロールを無効
          mapTypeControl: false, // 地図タイプ（地図や航空写真など）を切り替える地図タイプのコントロールを無効
          streetViewControl: true, // ユーザーがストリートビュー パノラマの有効
          streetViewControlOptions: {
            position: google.maps.ControlPosition.LEFT_BOTTOM
        },
          zoomControl: true, // ズーム コントロールの有効
          zoomControlOptions: {
            position: google.maps.ControlPosition.LEFT_BOTTOM
        },
          scaleControl: true, // 簡単な地図の縮尺調整機能を表示するスケール コントロールを有効
      });
    })
    .catch(e => {
      console.error(e);
    });
  },

  methods: {
    addressSearch () {
      const vm = this;
      const geocoder = new this.google.maps.Geocoder();
      if (geocoder) {
        // コールバックでは、results と status のコードをこの順序で保持する 2 つのパラメータを渡す必要がある
        geocoder.geocode({address: this.searchText }, function(results, status) {
          if(vm.marker){
            // Maps JavaScript APIの公式ドキュメントによるとMarker.setMap(null)で削除できるらしい.. がObjectの関連は消えるが地図上からは消えなかった。。
            // 事前にMarker.setVisible(false)することで解決(trueなら表示状態、falseなら非表示状態になる。)
            vm.marker.setVisible(false);
            vm.marker.setMap(null);
            vm.marker = null;
          }
          if (status === vm.google.maps.GeocoderStatus.OK) {
            // マーカーの設定
            vm.marker = new vm.google.maps.Marker({
              map: vm.map,
              position: results[0].geometry.location,
              draggable: false,
            });
            // 地図の位置座標をセットすると同時に、表示もその位置に変更する
            vm.map.setCenter(results[0].geometry.location);
          }
        });
      }
    }
  }
}
</script>
```

# ハマりポイント
## InvalidValueError: initMap is not a functionの対応

```
message: "Map: Expected mapDiv of type HTMLElement but was passed null."
name: "InvalidValueError"
```

ページを読み込んだ際に発生するエラー

原因：Google Maps のスクリプトを非同期で読み込んでいるため、
mountedを宣言したときに存在していない場合がある？

対策：読み込みタイミングをずらす

例）

``` javascript
window.onload = function () {
  処置内容
}
```


# 参考リンク
公式ガイド：https://developers.google.com/maps/documentation/javascript