Rails & kaminariで無限スクロール（Infinite Scroll）する

RailsでInfiniteScrollしたので書きます。
現在ロードされているコンテンツを一番下まで見たら続きがロードされるあれです。

## 使ったもの
Rails 5.2
[kaminari(https://github.com/kaminari/kaminari)](https://github.com/kaminari/kaminari)
[InfiniteScroll(https://github.com/metafizzy/infinite-scroll)](https://github.com/metafizzy/infinite-scroll)

## やったこと

1. 適当なデータを250レコード登録
2. 1ページあたり50件のページネーション実装
3. InfiniteScrollする

できたもの

![infinite.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/255661/8b9ba712-d059-b429-636d-8f375b1fee9e.gif)

## モデル

[こちら](https://ruby-rails.hatenadiary.com/entry/20141115/1416021886)よりモデルをお借りしました。

scaffoldでモデル作成

```ruby
rails g scaffold Shop name zipcode address tel 
```

```ruby:create_shops.rb
class CreateShops < ActiveRecord::Migration[5.2]
  def change
    create_table :shops do |t|
      t.string :name
      t.string :zipcode
      t.string :address
      t.string :tel

      t.timestamps
    end
  end
end

```

seeds.rb書く

``` ruby:seeds.rb
250.times do |i|
  Shop.create!(
    name:    "店名#{i}",
    zipcode: "111-#{sprintf("%04d", i)}",
    address: "住所#{i}",
    tel:     "00-1234-#{sprintf("%04d", i)}"
  )
end

```

データ作成

``` ruby
rails db:migrate
rails db:seed
```

で250件のshopレコードができました。

## kaminari導入

```ruby:Gemfile
gem 'kaminari'
```

bundle installしたらscaffoldで作ったshops_controllerとviewをいじります。

```ruby:shops_controller.rb
class ShopsController < ApplicationController
  before_action :set_shop, only: [:show, :edit, :update, :destroy]

  # GET /shops
  # GET /shops.json
  def index
    @shops = Shop.page(params[:page]).per(50) #1ページあたり50件表示
  end

  # 以下略

```

```slim:index.html.slim
h1 Listing shops

#shops
  = render partial: 'shop', collection: @shops
= paginate @shops
```

※ slimです。他のテンプレートエンジンならいい感じに読みかえてください。
デフォルトで書かれてるものは全部消してこれに置き換えれば大丈夫です。

``` = render partial: 'shop' ``` で呼んでるpartialはこちら

```slim:_shop.html.slim
.shop
  = "#{shop.name}: #{shop.zipcode}"
```


ここまでで、こんな感じのページネーションが完成するはずです。
<img width="379" alt="スクリーンショット 2018-10-14 15.17.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/255661/af0009a5-2aa9-8186-6666-f8c4bd31f86b.png">

そしたら次はInfinite Scrollです。

## Infinite Scroll

[InfiniteScroll(https://github.com/metafizzy/infinite-scroll)](https://github.com/metafizzy/infinite-scroll)から` infinite-scroll.pkgd.min.js `をもらってきて、` /app/assets/javascripts/ ` に置きます。（読めるとこならどこでもいいです）

したらapplication.jsにrequireを追加します。

```javascript:application.js
//= require infinite-scroll.pkgd.min
```

これで準備完了です。あとはこいつの動作をさっきのviewに合わせて、scaffoldでできたshops.coffeeに書いていきましょう。
coffeeじゃなくて普通のjs書くならいい感じに（略）

```javascript:shops.coffee
$(document).on 'turbolinks:load', ->
  $('#shops').infiniteScroll
    path: "nav.pagination a[rel=next]"
    append: ".shop"
    history: false
    prefill: true
    status: '.page-load-status'
```

ここまで書けばInfiniteScrollは動作するはずです。冒頭に貼ったgifとほぼ同じようになっていることと思います。

以下解説です。

- infiniteScrollのセレクタには監視する対象を指定します。
- pathには次のページへのリンクを指定します。今回はkaminariの` next > `のセレクタを指定しています。
- appendには、読み込んだ次ページの内容のうち、「この要素を対象としてappendする」ってのを指定します。` // append .post elements from next page to container ` (原文)
  - 今回は` .shop `としているので、読み込まれたpartialのうち ` .shop `以下のみがappendされます。他に余計なものがあったとしてもそれをappendしないようにできるんですね。
- historyは、読み込むたびにURLを書きかえるか指定します。``` history: 'push' ```にすると、読み込みのたびに``` ?page= ```の値が変化していきます。
- prefillはスクロールしきらないうちから読み込んじゃうかってやつです。これfalseにすると、一番下までスクロールしてからロードが始まるようになります。
- statusは読み込み中や全部読み込んだ後に表示するものの設定です。

## statusについて

InfiniteScrollのオプションで設定した ``` status: '.page-load-status' ```について。

の前に、viewを変更しましょう。

```slim:index.html.slim
h1 Listing shops

#shops
  = render partial: 'shop', collection: @shops
.page-load-status
  .infinite-scroll-request
    | loading!!
    br
    = paginate @shops
  p.infinite-scroll-last
    | end of contents!!!
  p.infinite-scroll-error
    | this is error message

```

公式のドキュメントにはこう記載があります。

> Displays status elements indicating state of page loading. Within the selected element:

>    .infinite-scroll-request element will be displayed on request
    .infinite-scroll-last element will be displayed on last
    .infinite-scroll-error element will be displayed on error

つまりそういうことです。
現在のロード状況に応じて、` .page-load-status `内の` .infinite-scroll-request `やら` .infinite-scroll-last `やらを出し分けてくれるんですね。素敵。

オプションはまだまだあるので、気になったら[こちら(https://infinite-scroll.com/options.html)](https://infinite-scroll.com/options.html)からどうぞ。

## 以上

ハッピースクロールライフを。