10
7

More than 1 year has passed since last update.

[Rails] Rails7のimportmapでGoogleMaps表示

Last updated at Posted at 2023-04-25

はじめに

  • Rails7で標準装備となっているimportmapを使用しGoogleMapsを表示させます。スクリプトを単体で読み込ませる方法と全体で読み込ませる方法について解説します。
  • importmap以外にもJavaScriptについても少々解説するので、"JavaScriptはこれから"という人にも分かりやすい内容になっているかもしれません。

実行環境

Ruby 3.2.2
Rails 7.0.4.3

やりたいこと

マーカーが配置された Google マップをウェブサイトに追加する
image.png

TL;DR(お急ぎの方)

まずはやりたいことを実現可能なコードを掲載し、後のパートで解説いたします。
GoogleMapsのスクリプトを読み込むには大きく以下2つのアプローチがあります。

  • ページ全体で読み込む
  • 特定ページのみで読み込む

ページ全体で読み込む

GoogleMapsをページ全体で読み込ませることは少ないと思いますが、他のスクリプトも同じような記述でページ全体で同様のスクリプトを読み込ませることができます。

注意:読み込みはページ全体で行っていますが、実行はGoogleMapsAPIを叩いている特定ページのみです。

1. jsファイルをjavascriptフォルダに配置

app/javascript/map.js
export function initMap() {
  const tower = { lat: 35.6586, lng: 139.7454 }; // 東京タワーを指定
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 18,
    center: tower,
  });
  const marker = new google.maps.Marker({
    position: tower,
    map: map,
  });
}

window.initMap = initMap

2. importmapにピン留め

config/importmap.rb
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "map" # 追加

3. Mapを埋め込むHTMLとinitMapを実行するスクリプトを記載

app/views/map/index.html.erb
<h3>My Google Maps Demo</h3>
<div id="map" style="height: 100vh; width: 100%;"></div>
<script
      src="https://maps.googleapis.com/maps/api/js?key=ENV['GOOGLE_MAP_API_KEY']&callback=initMap&v=weekly"
      defer
    ></script>

4. エントリーポイントで関数読み込み

app/javascript/application.js
import "@hotwired/turbo-rails";
import "controllers";
import "map"; // 追加

特定ページのみで読み込む

GoogleMapsは一般的には特定のページのみで表示させたいニーズが多いかと思います。

1. jsファイルをjavascriptフォルダに配置

app/javascript/map.js

function initMap() {
  const tower = { lat: 35.6586, lng: 139.7454 }; // 東京タワーを指定
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 18,
    center: tower,
  });
  const marker = new google.maps.Marker({
    position: tower,
    map: map,
  });
}
window.initMap = initMap;

2. importmapにピン留め

config/importmap.rb
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "map" # 追加

3. Mapを埋め込むHTMLとmap.jsを個別で読み込むためのヘルパーメソッドを記載

app/views/map/index.html.erb
<% content_for :js do %>
  <%= javascript_import_module_tag "map" %>
<% end %>

<h3>My Google Maps Demo</h3>
<div id="map" style="height: 100vh; width: 100%;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=ENV['GOOGLE_MAP_API_KEY']&callback=initMap&v=weekly"
      defer></script>

4. content_forをyeild

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head><%= javascript_importmap_tags %>
    <%= yield(:js) %> #追加
  </head></html>

解説

importmapについて軽く紹介し、コードを解説します。

Import Mapsとimportmap

Import Mapsは、そもそもRailsでなくJavaScript ES6で提供している機能で、モジュールのエイリアスとパスを紐づけ(マッピング)ます。これまではモジュールをインポートするためにはimport "./map.js";のようにインポート元のパスを書く必要がありましたが、Import Mapsを利用することによってimport "map";のように記述することができます。ES6単体ではheadタグ内に以下のscriptを記述することでモジュールをマッピングすることができます。Rails7で導入されたimportmapではこれらのタグを生成するヘルパーメソッドなどを提供しています。

<head>
	<script type="importmap" data-turbo-track="reload">{
	  "imports": {
			
	    "map": "/assets/map/map-549e7b70a8aee6f70d68a636f7efc4d5cf27b058b36c4a32de8655c7a6a1e054.js",
			
	  }
	}</script><script type="module">import "application"</script>
	<script type="module">import "map"</script>
</head>

ページ全体で読み込む(解説)

importmapが何者かざっくりわかったところで、前述のコードについて解説していきます。

1. jsファイルをjavascriptフォルダに配置

app/javascript/map.js
export function initMap() {
	
}
window.initMap = initMap;

exportと記述することで外部からもinitMap関数が呼べるようにしています。また、initMap関数はインポートしただけではコールバック関数として呼び出すことができないため、一番大きなスコープであるグローバル関数として登録します。

2. importmapにピン留め

config/importmap.rb
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "map" # 追加
pin "map", to: "map/map.js" # 相対パスで指定する場合

ここではマッピングする内容を一括して記載します。pin “map”を追記することでjavascriptディレクトリに配置したmap.jsを”map”というエイリアスでインポートできるようになります。app/javascript/map/map.jsに配置する場合など、他フォルダに存在する場合は相対パスで記載します。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head><%= javascript_importmap_tags %>
  </head></html>

# 生成されたHTML
<head>
	<script type="importmap" data-turbo-track="reload">{
	  "imports": {
			
	    "map": "/assets/map/map-549e7b70a8aee6f70d68a636f7efc4d5cf27b058b36c4a32de8655c7a6a1e054.js",
			
	  }
	}</script><script type="module">import "application"</script>
</head>

javascript_importmap_tagsヘルパーにて生成されたHTMLを確認すると、先ほどマッピングした内容でインポートするscriptタグを生成しています。importmap属性のscript内では、JSON形式で”map”というエイリアスでアセット化されたmapスクリプトを参照していることがわかります。また、スクリプト実行にあたり、module属性のscript内でapplication.jsがエントリーポイントとしてインポートされます。

3. Mapを埋め込むHTMLとinitMapを実行するスクリプトを記載

app/views/map/index.html.erb
<h3>My Google Maps Demo</h3>
<div id="map" style="height: 100vh; width: 100%;"></div>
<script
      src="https://maps.googleapis.com/maps/api/js?key=ENV['GOOGLE_MAP_API_KEY']&callback=initMap&v=weekly"
      defer
    ></script>

scriptタグ内のsrc属性ではAPIキーを環境変数dotenv経由で取得するようにしています。
また、defer属性をつけることで、HTMLや他のスクリプトの読み込みが完了した後にGoogleMapsAPIを叩きます。このdefer属性がないと、コールバック関数であるinitMapの実行に失敗してしまいます。

4. エントリーポイントで関数読み込み

app/javascript/application.js
import "@hotwired/turbo-rails";
import "controllers";
import "map"; # 追加

mapスクリプトをインポートします。application.jsでインポートするのは、同ファイルがあらゆるスクリプトを実行するエントリーポイント(起点)となっているからです。

特定ページのみ読み込む(解説)

1. jsファイルをjavascriptフォルダに配置

app/javascript/map.js
function initMap() {
  
}
window.initMap = initMap;

今回はapplication.jsなど他のスクリプトから参照することはないため、exportは不要です。

2. importmapにピン留め

config/importmap.rb
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "map" # 追加
pin "map", to: "map/map.js" # 相対パスで指定する場合

ページ全体で読み込む場合と同様にマッピング内容を設定します。

3. Mapを埋め込むHTMLとmap.jsを個別で読み込むためのヘルパーメソッドを記載

app/views/map/index.html.erb
<% content_for :js do %>
  <%= javascript_import_module_tag "map" %>
<% end %>

<h3>My Google Maps Demo</h3>
<div id="map" style="height: 100vh; width: 100%;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=ENV['GOOGLE_MAP_API_KEY']&callback=initMap&v=weekly"
      defer></script>

# 生成されたHTML
<head>
	<script type="importmap" data-turbo-track="reload">{
	  "imports": {
			
	    "map": "/assets/map/map-549e7b70a8aee6f70d68a636f7efc4d5cf27b058b36c4a32de8655c7a6a1e054.js",
			
	  }
	}</script><script type="module">import "application"</script>
	<script type="module">import "map"</script>
</head>

ページ全体で読み込む場合と異なる箇所としてcontent_forメソッドを追加しています。引数に任意のシンボルを取り(ここでは:js)、ブロックにjavascript_import_module_tagヘルパー引数”map”を渡すことで、map.jsを読み込むHTMLを作成しています。(正確には後述のyieldが必要)

4. content_forをyeild

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head><%= javascript_importmap_tags %>
    <%= yield(:js) %> #追加
  </head></html>

content_forのコンテンツを呼び出すためyieldしています。2つの組み合わせにより、該当ページでのみ<script type="module">import "map"</script>が生成され、スクリプトを呼ぶことができます。

さいごに

importmapには他にもCDNを読み込む機能なども提供しています。
以下が参考になりましたので合わせて読むことをおすすめします。

Rails 7: importmap-rails gem README(翻訳)
Rails 7.0 で標準になった importmap-rails とは何なのか?
Rails 7 でファイルごとに JavaScript を分けて使えるようにする

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7