microCMS

拡張フィールド(iframe連携)でGoogle Mapsと連携し、「地図フィールド」を実現する

この記事は公開後、1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。ひまらつです。

microCMSには「テキスト」「リッチエディタ」「数値」など、さまざまな種類のフィールドが用意されています。
コンテンツに合わせて最適なものを選べるようになっていますが、その一つに「インラインフレーム」フィールドがあるのはご存知でしょうか?

インラインフレーム(iframe)は外部のデータと連携するためのフィールドです。
例えば自社のデータベースと連携したり、APIを使って外部サービスと連携したりなどが実現できます。

-----
(追記)
2022/5/19より、iframe連携機能(インラインフレームフィールド)は「拡張フィールド」に名称変更しました。
【お知らせ】iframeフィールドの名称を拡張フィールドに変更します
-----

この記事ではインラインフレームを利用し、地図系のコンテンツをなめらかに扱う方法を紹介します。

1. 今回つくるもの:地図フィールド

インラインフレーム連携を使って、以下のようにコンテンツを入稿できます(動画):

上の動画ではマップから地点を選択してデータを入稿しています。
例えば東京タワーの地点を選択すると、microCMSのAPIレスポンスは次のようになります。

location の中身がマップで選択した地点のデータになっています。

この「地図フィールド」はどういったシーンで役立つでしょうか?
例えば店舗一覧のページを作る場合、

  • 「店舗名」
  • 「住所」
  • 「緯度・経度」

がデータとして必要になりますが、これを自分で調べて入力するのは手間ですよね。
microCMSの管理画面内でマップを選択して入稿が完結すれば、業務の効率は大幅に改善します。

それでは、実際にどのようにデータ連携を実装しているのか、コードを交えて紹介していきます。

2. インラインフレーム連携の仕組み

インラインフレーム連携では、iframeとmicroCMS管理画面の間で window.postMessage を使って値のやり取りを行います。
図にすると次のような流れになります。

今回はGoogle Mapsと連携させたいので、まずGoogle Maps APIを使った地図ページを作成します。
このページではクリックした地点の情報を取得する機能に加え、利便性向上のために簡単な地点検索も実装しておきます。

そしてその地図ページをiframeでmicroCMSと連携させます。
マップで選んだ座標や住所がmicroCMSのAPIから取得できるようになります。

3. 地図ページを作る

まずは地図ページの実装です。ここではVite + Reactのプロジェクトを新規に作成して進めていきます。
このパートはmicroCMSからは独立した部分ですので、興味のない方はされている方は「4. microCMSとデータ連携する」までスキップしていただいて構いません。

プロジェクトを作成する

ViteのGetting Startedページに沿って進めていきます。


いくつか項目を聞かれるので以下のように選択します。

  • プロジェクト名:iframe-sample
  • フレームワーク:react
  • 種類:react


プロジェクトが作成されます。
yarn dev を実行して開発サーバーを立ち上げたら、ブラウザでhttp://localhost:3000 にアクセスしましょう。

このようなサンプルページが表示されるはずです。

それでは App.jsx のファイルを編集して、地図ページを実装していきましょう。

Google Mapsを表示する

まず、Google Mapsを扱うために @react-google-maps/api を利用します。

yarn add @react-google-maps/api

Google Mapsを表示します。

// App.jsx
import { GoogleMap, LoadScript, Marker } from "@react-google-maps/api";

const containerStyle = {
    width: "100%",
    height: "100vh",
  };

// マップの初期情報
const defaultMapData = {
  center: {
    lat: 35.69575,
    lng: 139.77521,
  },
  zoom: 9,
};

const [mapData, setMapData] = useState(defaultMapData);

// マップを表示
return (
  <div className="App">
    <LoadScript googleMapsApiKey="<YOUR-GOOGLE-MAPS-API-KEY>">
      <GoogleMap
            mapContainerStyle={containerStyle}
            center={mapData.center}
            zoom={mapData.zoom}
          />
    </LoadScript>
  </div>
  );

<YOUR-GOOGLE-MAPS-API-KEY> には自身のAPI Keyを設定してください。
Google Maps の API Key の取得については以下のページが参考になります。


ページを表示してみて、地図が表示されていればOKです。

クリックした地点にマーカーを立てる

マップ上でクリックされた場所の地点を保持し、その場所にマーカーを立てます。

まずは値を保持するためのステートを用意します。

const [latlngData, setLatLngData] = useState(null);

先ほどのGoogle Mapsの部分を以下のように変更します。

<GoogleMap
  onClick={onMapClick}
  mapContainerStyle={containerStyle}
  center={mapData.center}
  zoom={mapData.zoom}
>
  {latlngData && <Marker position={latlngData.data} />}
</GoogleMap>

マップクリックに呼ばれる関数は以下のように宣言します。

const onMapClick = (event) => {
    const geocoder = new window.google.maps.Geocoder();
    geocoder.geocode({ location: event.latLng }, (results, status) => {
      if (status === "OK") {
        setLatLngData({
          status: "update",
          data: {
            address: results[0].formatted_address,
            lat: results[0].geometry.location.lat(),
            lng: results[0].geometry.location.lng(),
          },
        });
      }
    });
  };

latLngData の形は少し複雑なので補足しておきます。
後ほどmicroCMS側とデータをやり取りする時のことを考慮して以下のような構造になっています。

{
  status:  "default" or "update",
  data: {
    address: 住所,
    lat: 緯度,
    lng: 経度
  }
}


ここまで実装すると、マップをクリックしてマーカーが立つようになっているはずです。

地点の検索機能を追加する

マップをスクロールして場所を探すのは大変なので、地点名から検索してジャンプする機能をつけましょう。

まずはクエリのステートを定義します。

const [query, setQuery] = useState(null);

このような形でViewをつくり、

<div id="data-search-area">
  <input
    class="input"
    type="text"
    value={query ?? ""}
    onChange={handleChange}
    onKeyPress={handleKeyPress}
  />
  <button class="search" onClick={handleSubmit}>
    検索
  </button>
</div>


地点名から住所や緯度経度を取得するための処理を geocode として宣言します。

const geocode = () => {
    const geocoder = new window.google.maps.Geocoder();
    geocoder.geocode({ address: query }, (results, status) => {
      if (status === "OK") {
        // データを更新
        setLatLngData({
          status: "update",
          data: {
            address: results[0].formatted_address,
            lat: results[0].geometry.location.lat(),
            lng: results[0].geometry.location.lng(),
          },
        });

        // 地図表示を更新
        setMapData({
          center: {
            lat: results[0].geometry.location.lat(),
            lng: results[0].geometry.location.lng(),
          },
          zoom: 15,
        });
      }
    });
  };


「検索」ボタンをクリックしたときに geocode を呼びましょう。

const handleSubmit = (event) => {
  geocode();
};

const handleChange = (event) => {
  setQuery(event.target.value);
};

const handleKeyPress = (event) => {
  if (event.key == "Enter") {
    geocode();
  }
};


最後に、App.css に以下の行を足して見た目を少しだけ整えます。

// App.css
#data-search-area {
  margin: 10px 0;
}

input.input {
  width: 240px;
  height: 24px;
  margin: 0 10px;
}

button.search {
  height: 30px;
}


地点を検索できるようになりました。


これで地図ページの実装は完了です。
このページをmicroCMSと繋ぎ込んでいきましょう。

4. microCMSとデータ連携する


ここでもう一度データ連携の流れを確認しましょう。
図中の矢印の①〜③がデータ連携部分になります。順に説明します。

①デフォルト値の取得

microCMSとデータをやり取りするために必要なIDが渡されます。これは今後利用するため保持する必要があります。
また、すでに値がセットされている場合は初期表示のためにそのデータも渡されます。

const [iframeId, setIframeId] = useState(null);

// ①デフォルト値の取得
useEffect(() => {
  window.addEventListener("message", (e) => {
    if (
      e.isTrusted === true &&
      e.data.action === "MICROCMS_GET_DEFAULT_DATA"
    ) {
      // IDを保持しておく
      setIframeId(e.data.id);

      // すでにデータがあれば初期表示に反映させる(マップの中心地を移動)
      if (e.data.message) {
        setLatLngData({ status: "default", data: e.data.message.data });
        setMapData({
          center: {
            lat: e.data.message.data.lat,
            lng: e.data.message.data.lng,
          },
          zoom: 15,
        });
      }
    }
  });
 }, []);

②マップの選択地点が変わったらmicroCMSに伝える

保持しておいた iframeId を使って、microCMS側にデータをPOSTします。

microcmsAdminUrl = "<YOUR_SERVICE_ID>.microcms.io"

 useEffect(() => {
    if (latlngData?.status === "update") {
      postDataToMicroCMS();
    }
  }, [latlngData]);

// ②マップの選択地点が変わったらmicroCMSに伝える
const postDataToMicroCMS = () => {
  window.parent.postMessage(
    {
      id: iframeId,
      action: "MICROCMS_POST_DATA",
      message: {
        data: latlngData.data,
      },
    },
    microcmsAdminUrl
  );
};

②' データ連携のフィードバックを表示する

データ連携のフィードバック表示を作っておきましょう。react-hot-toast を利用します。

import toast, { Toaster } from "react-hot-toast";


先ほど実装した addEventListener("message", ... に追記し、成功時のフィードバックを受けられるように修正します。

const notify = (test) => toast.success(test);

useEffect(() => {
  window.addEventListener("message", (e) => {
    if (
      e.isTrusted === true &&
      e.data.action === "MICROCMS_GET_DEFAULT_DATA"
    ) {
       // ... (略)
    } else if (
      e.isTrusted === true &&
      e.data.action === "MICROCMS_POST_DATA_SUCCESS"
    ) {
      // トーストでフィードバック
      notify("microCMSにデータを連携しました");
    }
  });
}, []);


Toast表示を追加します。

return 
(
  <div className="App">
      // 追加
      <Toaster position="top-right" toastOptions={{ duration: 1000 }} />
      ...
  </div>
);

microCMSにデータ連携したタイミングでトーストが表示されるようになりました。

③iframeフィールドの大きさを変更する

フィールドの大きさを変更したい場合、以下のような postMessage を送ることでサイズを変更できます。

useEffect(() => {
  window.parent.postMessage(
    {
      id: iframeId,
      action: "MICROCMS_UPDATE_STYLE",
      message: {
        height: 640,
        width: "100%",
      },
    },
    microcmsAdminUrl
  );
}, [iframeId]);


これでmicroCMSとのデータ連携を実装できました。

5. microCMS管理画面から使ってみよう

作成したページをmicroCMSと連携しましょう。

APIスキーマを定義する

APIを新しく作成します。

フィールドの種類で「インラインフレーム」を選択します。

モーダルが表示されるのでURLを入力します。
サーバーにホスティングしているURLでも構いませんし、localhost を指定することもできます。


これでAPIが準備できたので、コンテンツを追加してみましょう。
入稿画面を見ると「場所」の項目には自作したWebサイトがiframeで表示されています。

地点を選択してみます。例えば「東京タワー」で検索して、位置情報を取得しました。

コンテンツを公開するとAPIのレスポンスは以下のようになります。

地図で選択した地点の latlngaddress が取得できていることが確認できます。
これでmicroCMSとGoogle Mapsが連携でき、「地図フィールド」を自作できました。

microCMSエンジニアからiframe連携の話が直接聞ける!オンラインイベントのお知らせ

4月21日(木)に開催する「microCMS Online Meetup」では、今回ご紹介したiframe連携OpenAPIについての機能詳細や、最新のアップデート情報についてmicroCMSメンバーからの発表を予定しています。ぜひお気軽にご参加登録ください👍

最後に

インラインフレームを使って外部のデータと連携する方法を紹介しました。
連携するためのページを自分で作る必要があるなど技術的なスキルは求められますが、外部のデータと繋げる大きな可能性があると思います。

インラインフレームでを試して何か作った際にはぜひTwitterなどで教えてください!

まずは、無料で試してみましょう。

APIベースの日本製ヘッドレスCMS「microCMS」を使えば、 ものの数分でAPIの作成ができます。

microCMSを無料で始める

microCMSについてお問い合わせ

初期費用無料・14日間の無料トライアル付き。ご不明な点はお気軽にお問い合わせください。

お問い合わせ

microCMS公式アカウント

microCMSは各公式アカウントで最新情報をお届けしています。
フォローよろしくお願いします。

  • X
  • Discord
  • github

ABOUT ME

ひまらつ
SwiftやPythonやスプラトゥーンを楽しんでます