思い付くまでタイトル未定

RubyとかRailsとかももクロちゃんとか色々な事書きます。

やればできる子!will_paginateで動的「もっと見る」を実装

「will_paginate」についてググっていると、「kaminariの方が使いやすい」「will_paginateはデザインが(ry」などの否定的な意見をよく目にします。
そういう時私は、

何がkaminariだよ!!使えるもんなら使いたいわ!!大人の事情ってもんがあるんだ!!

って思う訳です。

今回の「もっと見る」の実装でも、kaminariの情報ばかり出てきて苦労したので火を噴くかと思いました。

早速やってみます。

今回の条件

  • rubyruby 1.9.3p547 (2014-05-14 revision 45962) [x86_64-linux]
    ↑大人の事情で古い。。。
  • railsRails 3.1.7
    ↑大人の事情で(ry
  • モデル:article.rb
  • コントローラー:portal_controller.rb
  • データ:articlesテーブルに6件

コントローラーでやること

portal_controller.rb

  1. indexアクションにpaginate設定します。

       def index
         @items = Article.paginate(:page => params[:page], :per_page => 2)
       end
    

    データが6件しかないので、per_pageは2にしてます。

  2. moreアクションに「もっと見る」リンクが押された時の処理を書きます。

       def more
         index
       end
    

と言っても、ただindexアクションに回すだけです。
この仕組みを使えば大人の事情があって、searchアクションとかの、indexアクション以外からも呼び出されるよって時は、
paramsにアクション名を持たせて、moreアクションからそのアクションを呼び出すようにできます。

ビューでやること

index.html.erb

  1. @itemsを表示する 部分テンプレートを呼び出します。

     <div id="items"><%= render 'items' %></div>  
    

    タグにidを付けるのが大事!

  2. 「もっと見る」リンクを表示する 部分テンプレートを呼び出します。

     <%= render 'more' %>
    

_items.html.erb

  1. データを表示します。

     <% @items.each do |item| %>
       <div>
         <%= item.title %>
         <%= item.body %>
       </div>
     <% end %>
    

_more.html.erb

  1. 「もっと見る」リンクを表示します。

     <% if @items.next_page %>
       <%= link_to "もっと見る", {:action=>"more", :page => @items.next_page}, :remote => true, id: 'more_link', :onclick => "nowLoading();" %>
     <% end %> 
    

    タグにidを付けるのが大事!
    以下ポイント!

    1. <% if @items.next_page %>を使って、次のページ(データ)がある時だけ「もっと見る」リンクを表示するようにします。

    2. :page => @items.next_pageでparamsに次のページの番号を渡します。

    3. :remote => trueを指定してAjaxで動的処理をします。

    4. :onclick => "nowLoading();"でクリック時にリンクを切るjs関数を呼び出します。(後述します。)

more.js.erb

  1. <div id="items"></div>に、@itemsを表示する部分テンプレートを追加します。

     jQuery('#items').append("<%= escape_javascript(render 'items', object: @items) %>");  
    
  2. 「もっと見る」リンクを置き直します。

     jQuery("#more_link").replaceWith("<%= escape_javascript(render 'more') %>");
    

ルーティングでやること

routes.rb

  1. indexアクションと、moreアクションを登録します。

       controller :portal do
         get "index" => :index
         get "more"  => :more
       end
    

    やり方はお任せ。

jsでやること

  1. 「もっと見る」リンクをクリックされたら、リンクを外します。

     function nowLoading(){
       $("#more_link").replaceWith("<a id='more_link'>読込中</a>");
     };
    

    これをやらないと、複数回クリックをした時に同じデータが何個も出てきてしまいます。大事!
    「読込中」とかにするとお洒落かも。

完成!

実際に使ってみたのがこちら↓
f:id:kanahebiZ:20150701170342g:plain

will_paginateさんはYDKでした。