RailsでjQueryを使ったSPAっぽい画面遷移無しの複数モデル追加読み込み

#これは何
1画面に複数モデルを表示するSPAっぽいRailsアプリで画面遷移無しで追加読み込みしたい、
でもReactやAngular ~~はよく分からない~~ を導入してる時間はない、
という人のための記事です。

GitHubに[作成済みのソースコード](https://github.com/nametaketakewo/multi-model-infinitscroll)があります。

#何を使うの

kaminariとjQuery, jquery.infinitscrollを使いモデルの追加読み込みを実装します。

#どうなるの

こんな感じになります。
![multi-model-infinitscroll.gif](https://qiita-image-store.s3.amazonaws.com/0/51329/464f3154-f9d5-28c2-7053-8aa8985d85da.gif)

#どうするの

##アプリの作成

```bash:コマンド
$ rails new multi-model-infinitscroll --skip-bundle &&
  cd multi-model-infinitscroll
```

##下拵え

###kaminariとjquery-turbolinksのインストール

```ruby:Gemfile
#追記
gem "jquery-turbolinks"
gem "kaminari"
```

```bash:コマンド
$ bundle install
```

###jquery.infinitscrollのダウンロード

```bash:コマンド
$ curl -k -o vendor/assets/javascripts/jquery.infinitescroll.js \
 https://raw.githubusercontent.com/paulirish/infinite-scroll/master/jquery.infinitescroll.js
```

###Asset Pipelineにjquery-turbolinksとjquery.infinitscrollを追加

```ruby:app/assets/javascripts/application.js
#追記
//= require jquery.turbolinks
//= require jquery.infinitescroll
```

##モデルの準備

###モデルの作成

```bash:コマンド
$ rails g model Hoge name description &&
  rails g model Piyo name description &&
  rails db:migrate
```

###初期データの投入

```ruby:db/seeds.rb
#追記
1000.times do |i|
  Hoge.create!(name: 'Hoge' + i.to_s, description: 'This is Hoge ' + i.to_s)
  Piyo.create!(name: 'Piyo' + i.to_s, description: 'This is Piyo ' + i.to_s)
end
```

```bash:コマンド
$ rails db:seed
```

##Controllerの準備

###メイン画面となるControllerを作成

```bash:コマンド
$ rails g controller home home
```

```ruby:app/controllers/home_controller.rb
class HomeController < ApplicationController
  def home

    #下記追記

    #普通のアクセスの時に実行
    @hoges = Hoge.page(params[:hoge_page]) if params[:piyo_page].blank?
    @piyos = Piyo.page(params[:piyo_page]) if params[:hoge_page].blank?
    #*_pageが渡された時はもう片方は不要なのでif文でDBへのアクセスを軽減

    #追加読み込み時に実行、レイアウト無しでデータのみを返す
    unless params[:hoge_page].blank?
      return render @hoges, layout: false
    end
    unless params[:piyo_page].blank?
      return render @piyos, layout: false
    end

  end
end
```

###ルーティングの設定

```ruby:config/routes.rb
Rails.application.routes.draw do
  get 'home/home' #要らないので任意で削除

  root to: 'home#home' #追記
end
```

###モデル読み込み用のControllerを作成

これはなくても動くので任意で作成してください。

```bash:コマンド
$ rails g controller hoges &&
  rails g controller piyos
```

##Viewの準備

###home viewの編集

```html+erb:app/views/home/home.html.erb
<div id='hoge_container'>
  <table id="hoges">
    <thead>
      <tr>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody class="page">
      <%= render @hoges %>
    </tbody>
  </table>

  <br>
  <%= paginate @hoges, remote: true, param_name: :hoge_page %>
</div>

<div id='piyo_container'>
  <table id="piyos">
    <thead>
      <tr>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>

    <tbody class="page">
      <%= render @piyos %>
    </tbody>
  </table>

  <br>
  <%= paginate @piyos, remote: true, param_name: :piyo_page %>
</div>
```

###hogeテンプレートの作成

```html+erb:app/views/hoge/_hoge.html.erb
<tr class="hoge">
  <td><%= hoge.name %></td>
  <td><%= hoge.description %></td>
</tr>
```

###piyoテンプレートの作成

```html+erb:app/views/piyo/_piyo.html.erb
<tr class="piyo">
  <td><%= piyo.name %></td>
  <td><%= piyo.description %></td>
</tr>
```

###cssの編集

```css:app/assets/stylesheets/application.css
/*追記*/
#infscr-loading, .pagination {
  width: 0;
  display: none;
}

#hoge_container, #piyo_container{
  float: left;
  margin: 10px 50px;
  padding: 10px 50px;
}

thead, tbody {
  display:block;
}

tbody {
  overflow-y:scroll;
  height:500px;
}

```

###infinitescrollの設定


behaviorsとbinderを指定する事でウィンドウではなく特定の要素が下までスクロールされた時に追加読み込みを実行します。

```ruby:app/assets/javascripts/home.coffee
#追記
$ ->
  $("#hoges .page").infinitescroll
    loading: {
      img: ""
      msgText: ""
      finishedMsg: ""
    }
    animate: true
    navSelector: "#hoge_container nav.pagination"
    nextSelector: "#hoge_container nav.pagination a[rel=next]"
    itemSelector: "tr.hoge"
    behaviors: "local"
    binder: $("#hoge_container .page")

$ ->
  $("#piyos .page").infinitescroll
    loading: {
      img: ""
      msgText: ""
      finishedMsg: ""
    }
    animate: true
    navSelector: "#piyo_container nav.pagination"
    nextSelector: "#piyo_container nav.pagination a[rel=next]"
    itemSelector: "tr.piyo"
    behaviors: "local"
    binder: $("#piyo_container .page")

```

##実行

```bash:コマンド
$ rails s
```
[localhost:3000/](http://localhost:3000/)にアクセスして動作を確認してみてください。

それぞれをスクロールして自動で追加読み込みされれば成功です。

#まとめ

JavaScriptフレームワークを勉強しましょう。

#参考

[Infinite Scroll](https://github.com/infinite-scroll/infinite-scroll)

[RailsでInfinite Scrollとkaminariを使いスクロールによる動的にページロード機能を実装する](http://ruby-rails.hatenadiary.com/entry/20141115/1416021886)

[【Ruby On Rails】infinit scrollとkaminariによる無限スクロールの実装](http://watarisein.hatenablog.com/entry/2016/01/02/013050)