Rails x Ajaxでスマートに「もっと見る機能」を実装する


はじめに

  • 巷でよく見るAjaxでの「もっと見る」機能を一つのページ内で複数種類設置する依頼が来て、色々調べたので共有。
  • 完成形としてはこんな感じになります。(見やすさ的に1つずつ出すようにしていますが、適宜数は変えられます)

  • 予備知識として以下のものを頭に入れておく必要があります。
  1. Kaminariによるページネーション。
  2. RailsのAjax通信。(index.js.erbのこととか)
  3. BootStrap4(ビューの整形に使っているだけなので、詳しくなくていいけども)
  4. Slimテンプレート(3と同様)

環境

$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

$ rails -v
Rails 5.2.1

準備

  • 以下のものを必要とします。
gem 'kaminari'
# seedを使うなら
gem 'seed-fu' 

実装

モデル

  • ここでは例としてDogCatを使いますが、存在としてあればいいので実装は割愛します。
  • seed-fuとかを使って、前もってページネーション用のインスタンスをDBに作っておきましょう。

コントローラー

  • まずはコントローラーから。今回のページをsample#indexアクションとします。
  • このアクションでは、htmlフォーマット(通常)jsフォーマット(Ajax)の二種類のリクエストを扱うことになります。
  • 通常アクセスの場合はguardreturn unless request.xhr?)で止まるので、インスタンス変数を受け取ってページをレンダーするだけです。
  • しかしAjax通信の場合はguardを通過して、params[:type]毎に、異なるJSファイルを実行します
  • JSファイルの中には、「特定の一覧リストに新しい要素をAppendして、特定の「もっと見る」ボタンを更新する」処理が入っているため、渡したパラメーターによって挙動を変えることができるのです。
class SampleController < ApplicationController
  def index
    @dogs = Dog.all.page(params[:page]).per(1)
    @cats = Cat.all.page(params[:page]).per(1)

    # これ以下はAjax通信の場合のみ通過
    return unless request.xhr?

    case params[:type]
    when 'dog', 'cat'
      render "sample/#{params[:type]}"
    end
  end
end

ビュー

  • ビューは今回の場合、全部で5つ用意します。
app/views/sample/
├── _cat.html.slim
├── _dog.html.slim
├── cat.js.erb
├── dog.js.erb
└── index.html.slim
  • 基本私はSlimを使っているのですが、JSファイルを書く際にはErbで書くことを強くお勧めします。(調べたらわかりますがとても描きにくい‥‥)
  • 余談ですが、Slim使ってても、拡張子にerbってつければ動いてくれるのでとても助かってます!

HTMLフォーマット側

/ index.html.slim
.container.my-5
  .row
    .col-md-6
      #dog-list
        = render partial: 'sample/dog', collection: @dogs
        = link_to_next_page @dogs, 'もっと見る', id: 'more-dog', class: 'btn btn-light w-100', params: { type: :dog }, remote: true
    .col-md-6
      #cat-list
        = render partial: 'sample/cat', collection: @cats
        = link_to_next_page @cats, 'もっと見る', id: 'more-cat', class: 'btn btn-light w-100', params: { type: :cat }, remote: true
/ _cat.html.slim, _dog.html.slim (cat -> dogに書き換えるだけ)
.border.mb-1
  p cat
  • パーシャルビューに関しては、存在が確認できればいいだけなので適当です。
  • ポイントは以下の通り。
    • それぞれのリストが入る大元のDivタグユニークなIDを付与。
    • link_to_next_pageメソッドにユニークIDtypeパラメーターを付与。

JSフォーマット側

/ cat.js.erb, dog.js.erb (cat -> dogに書き換えるだけ)
$('#more-cat').remove();
$('#cat-list').append("<%= j render partial: 'sample/cat', collection: @cats %>")
$('#cat-list').append("<%= j link_to_next_page(@cats, 'もっと見る', id: 'more-cat', class: 'btn btn-light w-100', params: { type: :cat }, remote: true) %>")
  • ご覧の通り、特定のリストに新しい要素を追加しているだけです。
  • Ajaxを呼ぶたびに「もっと見る」ボタンが更新され、もしこれ以上データがないようであれば自動でレンダーされなくなります神かよ

おわりに

  • こんな感じで想像以上にすっきりとAjaxでの「もっと見る」を実装することができるようになりました。
  • 今回は1ページに「もっと見る」が二つという特殊な場合を扱いましたが、別に一つでも全然動きます。
  • 皆様良いRailsライフをー👍