画像複数投稿機能(swiperの導入)

内容

画像複数投稿機能の実装

画像複数投稿機能

画像単一投稿から複数枚投稿に編集する

1. アソシエーションを変更

has_many_attachedメソッドを用いることで、投稿と画像の間に1対多の関係を設定
app/model/post.rb

class Post < ApplicationRecord
  has_many_attached :images
end

2. フォームを編集

複数の画像を送信できるように、フォームを編集
単一画像の投稿では:imageとなっていたが、複数の画像に対応できるように編集し、複数投稿できるようにするため、multiple: trueを使用
app/views/posts/new.html.erb

<%= form_with model: @post_address, url: posts_path, local: true do |f| %>
  <%= f.file_field :images, multiple: true, class:"map-image", id:"map-image" %>
<% end %>

3. ストロングパラメーターを編集

送信されてきたparamsをコントローラで受け取り、こちらも画像の配列を受け取れるように変更
app/controllers/posts_controller

private

def post_params
  params.require(:post_address).permit(:map_link, {images: []}, :distance, :course, :slope, :traffic, :crowd, :view,
                                         :comment, :postal_code, :prefecture_code, :city, :street).merge(user_id: current_user.id)
end

4. 保存した画像を表示できるようにする

app/views/posts/index.html.erb

<% post.images.each do |image| %>
  <%= image_tag image, class: 'post-image' if post.image.attached? %>
  <%= image_tag 'noimage.png', class: 'post-image' unless post.image.attached? %>
<% end %>

5. スライダー機能「swiper」を導入

公式サイトを参考にswiperを導入する
swiper
やり方はいくつかあるが、今回はCDNでswiperを読み込む(単体で動くのでjQuery本体を読み込む必要はない)
application.html.erb

<head>
  <link rel="stylesheet" href="https://unpkg.com/swiper@7/swiper-bundle.min.css"/>
  <script src="https://unpkg.com/swiper@7/swiper-bundle.min.js"></script>
</head>

6. HTMLを編集

公式サイトを引用し、必要なものを追加(今回スクロールバーは使わず)
一覧ページと詳細ページでスワイパーを使用するため、部分テンプレートに記述する
_swiper.html.erb

<% if post.images.length > 1 %>
  <div class='swiper'>
    <div class="swiper-wrapper">
      <% post.images.each do |image| %>
        <div class="swiper-slide">
          <%= image_tag image, class: 'swiper-image' if post.images.attached? %>
        </div>
      <% end %>
    </div>
    <div class="swiper-pagination"></div>
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>
  </div>
<% elsif post.images.length == 1 %>
  <div class='back-image'>
  <% post.images.each do |image| %>
    <%= image_tag image, class: 'post-image' if post.images.attached? %>
  <% end %>
  </div>
<% else %>
  <div class='back-image'>
    <%= image_tag 'noimage.png', class: 'post-image' unless post.images.attached? %>
  </div>
<% end %>

上記の条件分岐により、投稿画像が2枚以上あればスワイパー機能を使用し、1枚以下であれば画像を貼り付けるだけとした

7. CSSを編集

swiper.cssを作成し、画像をスワイプできるように実装
swiper.css

/* どんな画像でも高さが一定になるように指定し、枠内に収まるように記述 */
.swiper-slide {
  height: 0;
  overflow: hidden;
  padding-bottom: 75%;
  background-color: #f2f2f2;
  position: relative;
}
.swiper-image {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  height: 100%;
  width: auto;
}
/* 矢印ボタンをわかりやすいものに変更(ネットで調べたものを引用) */
.swiper-button-prev {
  background: url(https://haniwaman.com/wp/wp-content/uploads/2018/05/swiper3.png) no-repeat center center / contain;
}
.swiper-button-next {
  background: url(https://haniwaman.com/wp/wp-content/uploads/2018/05/swiper4.png) no-repeat center center / contain;
}
/* デフォルトの矢印ボタンを消去(!importantは、cssのどこに記述しても優先順位が先になるという記述) */
.swiper-button-prev::after, .swiper-button-next::after {
  content: none !important;
}
/* 画像が1枚以下の時は以下の記述で、背景画像とその枠内に画像を収めるようにする */
.back-image {
  height: 0;
  overflow: hidden;
  padding-bottom: 75%;
  background-color: #f2f2f2;
  position: relative;
}
.post-image {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  height: 100%;
  width: auto;
}

画像が2枚以上の時と1枚以下の時でclass名を同じにしたらビューが崩れてしまったため、別々にclassを指定し同じcssを記述した
親要素(div)が影響している?

8. jsを編集

swiper.jsを作成し、application.jsで読み込む
swiper.jsには公式サイトで記述されていたコードを引用し、使用する動作により修正を加える
application.js

require("../swiper")

swiper.js

// 公式サイトの記述だけでは矢印ボタンを押してもスワイプできなかったので1行目を追記
window.addEventListener('DOMContentLoaded', function(){
  const swiper = new Swiper('.swiper', {
    // 画像をループさせる
    loop: true,
    // 何枚目の画像かわかるようにし、クリックによるページ遷移を有効にする
    pagination: {
      el: '.swiper-pagination',
      type: 'bullets',
      clickable: true,
    },
    // ページ遷移させる
    navigation: {
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev',
    },
  });
});

所感

swiperもしくはslickを用いてスライダー機能の実装を試みた
ネットにはかなり情報があったが、バージョンが古かったり、自分のコードとは少し異なったりで、導入するのにかなり苦戦した
動かない時はなぜ動かないのかを理解できるようにならないと、今後も同じように時間がかかってしまうと思った
日々勉強