概要
一覧の写真をクリックすると、モーダルウィンドウ内に写真詳細画面が表示される機能を実装します。
参照
モーダルウィンドウ
基本的なモーダルウィンドウの作成方法とアニメーションの付け方は、これらの記事を参考にしました。
-
JavaScriptでモーダルウィンドウを作ろう(ドットインストール)
https://dotinstall.com/lessons/modal_js_v3 -
モーダルウィンドウ・ダイアログウィンドウの作り方1(HTML、CSS、JavaScriptで作る)
https://www.youtube.com/watch?v=BZX6RxQzgrQ -
モーダルウィンドウ・ダイアログウィンドウの作り方2(HTML、CSS、JavaScriptで作る)
https://www.youtube.com/watch?v=AXVaTCCs7Ew&t=250s
Ajax
remote: trueによるAjaxの実装方法は、この記事がわかりやすいです。
- 【Rails】remote: trueでフォーム送信をAjax実装する方法とは?
https://pikawaka.com/rails/remote-true
完成イメージ
indexビューの写真をクリックするとshowビューの写真詳細画面がモーダル内に表示される。
開発環境
macOS Big Sur バージョン 11.2
ruby 2.6.5
Rails 6.0.3.4
実装の流れ
- 仮のモーダルウィンドウを作成する。
- 写真クリックでshowアクションが実行され、js.erbファイルが呼び出されるようにする。
- JavaScriptにより、モーダルウィンドウ内にshowビューが格納され表示されるようにする。
実装
1. 仮のモーダルウィンドウを作成する。
indexとshowアクションはphotosコントローラーで定義されています。
class PhotosController < ApplicationController
省略
def index
省略
end
def show
省略
end
省略
end
index.html.erbの適当な位置に仮のモーダルウィンドウを設置します。
hiddenクラスで最初は表示されないようにします。
# モーダルの周囲を薄暗くするマスク
<div id="mask" class="hidden"></div>
# モーダル
# あとからJavaScriptで<div>内のコードをshowビューのコードに置き換えます。
<div id="modal" class="hidden">
<p>モーダルウィンドウ<p>
</div>
cssはこのようになります。詳細は参照動画を御覧ください。
/* hiddenクラスがないとマスクとモーダルが表示される(JavaScriptでhiddenクラスを消したとき)*/
#mask {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1;
}
#modal {
position: fixed; /* スクロールで移動しないようmodalの位置を固定します */
top: 10vh;
/* 以下3行:モーダルを左右中央揃えにする方法 */
left: 0;
right: 0;
margin: 0 auto;
transform: translate(0, 0) scale(1);
transition: 0.2s; /* transitionでアニメーションを作成 */
z-index: 2; /* マスクより上に重なるようにする */
width: 935px; /* showビューの幅に合わせて設定 */
}
/* hiddenクラスで最初は表示されないようにする */
#mask.hidden {
opacity: 0;
visibility: hidden;
}
#modal.hidden {
opacity: 0;
visibility: hidden;
transform: translate(0, 0) scale(0.8); /* 大きさ0.8倍から等倍にモーダルが拡大される */
}
2. 写真クリックでshowアクションが実行され、js.erbファイルが呼び出されるようにする。
一覧の写真は部分テンプレートにしてあります。
link_to
メソッドでshowアクションが実行され、remote: true
を設定することにより、リクエストをjs形式にします。
# 部分テンプレートを呼び出す
<%= render partial:"shared/photos", locals: {photos: @photos} %>
<% photos.each do |photo| %>
# 写真をクリックしたときの実行アクション(showなのでphoto_path(photo.id))とリクエスト形式(jsなのでremote: true) を指定
<%= link_to photo_path(photo.id), remote: true do %>
<%= image_tag photo.image.variant(省略), class: "photo-img", id: "photo-img" %>
<% end %>
<% end %>
class PhotosController < ApplicationController
省略
def show
省略
respond_to do |format|
format.html
# link_toメソッドをremote: trueに設定したのでリクエストはjs形式で行われる(詳しくは参照記事をご覧ください)
format.js
end
end
省略
end
3. JavaScriptにより、モーダルウィンドウ内にshowビューが格納され表示されるようにする。
showビューを取得するために、show.html.erbを部分テンプレート化します
(省略)(中身はshow.html.erbと同じ)
上記showアクションが実行されると、photosフォルダ内のshow.js.erbファイルが呼び出されます。ない場合は作成します。
# モーダルを取得
var modal = document.getElementById('modal');
# マスクを取得
var mask = document.getElementById('mask');
# モーダルの中身をshowビューに置き換えます。
# 部分テンプレートのパス、localsの値はアプリにより異なります。
modal.innerHTML = '<%= escape_javascript(render partial: 'shared/photo_show', locals: { photo: @photo, comments: @comments, user: @user, comment: @comment }) %>';
# hiddenクラスを消してモーダルとマスクを表示させます。
modal.classList.remove('hidden');
mask.classList.remove('hidden');
# モーダルの外側(マスク)をクリックするとhiddenクラスが書き込まれ、モーダルとマスクが再び非表示となります。
mask.addEventListener('click', () => {
modal.classList.add('hidden');
mask.classList.add('hidden');
});
おわりに
注意点として、モーダル内で"いいね"や"コメント投稿"などを実行する場合は、いずれもAjax化しておく必要があります。Ajax化されていない場合、それらを実行すると他のページにリダイレクトされたりエラーとなったりします。
↓Ajax化されていると、このようにモーダル内で"いいね"や"コメント投稿"ができます。
以上、参考になりましたら幸いです。
間違いなどありましたら、ご指摘いただきたいです。