Rails のページネーションと検索機能 - Gem なし

19439 ワード

今日は、外部 gem を使用せずに URL パラメーターを使用して、Rails アプリにページネーションを追加する方法について説明します.

ファイルの作成



開始するには、コントローラーを作成し、ルート ルートを設定する必要があります.便宜上、レール ジェネレーターを使用して、ターミナルで rails g controller Home index を実行します.ホームの代わりに、コントローラーに別の名前を自由に使用してください. Rails はいくつかのファイルを生成し、それらがどこにあるかの詳細な要約を提供します.

 create  app/controllers/home_controller.rb
  route  get 'home/index'
 invoke  erb
 create    app/views/home
 create    app/views/home/index.html.erb
 invoke  test_unit
 create    test/controllers/home_controller_test.rb
 invoke  helper
 create    app/helpers/home_helper.rb
 invoke    test_unit
 invoke  assets
 invoke    scss
 create      app/assets/stylesheets/home.scss


オプション: config > routes.rb に移動し、get 'home/index'root 'home#index' に置き換えることで、home#index をルート パスとして設定します.

コントローラーのセットアップ



デモンストレーションの目的で、実際のデータベースを使用する代わりにクラス メソッド #database を使用しています.

class HomeController < ApplicationController
  # number of results to be shown per page
  @@results_per_page = 2 
  def index
    # params will be used in views for input and links
    @params = params.permit(:query, :page)
    # current page will be 1 by deault unless
    # user clicks on another page 
    @current_page = params[:page].try(:to_i) || 1
    # create a fetch request to database and retrieve the results
    @results, @pages_count  = database(@params, @current_page)
  end

  # sample database
  def database(params, current_page)
    # dummy data
    data = [ 'apple', 'cake', 'candy apple', 'car',
      'friend', 'run', 'hunt', 'ship', 'tip'
    ]
    # filter data using provided parameters. You can filter the data 
    # with as many parameters as you'd like
    results = data.filter{|i| i.include? params[:query].to_s} 
    # total number of pages that should be displayed
    pages_count = (results.length/@@results_per_page.to_f).ceil

    # results to display based on current page and result limit
    start = @@results_per_page * (current_page - 1)
    results = results[start, @@results_per_page]
    [results, pages_count]
  end
end


Active Record を使用している場合、コードは次のようになります.選択したページの結果のみが返されることに注意してください.作成する必要があるページ アイテムの数を計算するために必要な、結果の総数を返すデータベースへの別の呼び出しを作成する必要がある場合があります.

SampleModel.where("name LIKE ?", "%#{query}%") 
  .limit(results_per_page)
  .offset(results_per_page * (current_page-1)) 


私たちの見解



ビューを 3 つのステップに分けて説明します.検索フィールド、ページネーション項目、および結果項目.

1. 検索欄



上部には、ユーザーがアイテムを名前で検索できるようにするフォームがあります.フォームは root_path に送信されます.検索フィールドの onkeydown 属性に JavaScript を追加しました.検索フィールドが空の場合、名前は割り当てられません.これにより、URL に query パラメーターが含まれなくなり、少しすっきりします.

*デモンストレーション用にインライン JavaScript を追加しました.ただし、javascript フォルダーに JavaScript を追加する方が適切です.または、イベント リスナーを追加して、送信前にフォーム入力を反復処理することもできます.

  <%= form_with url: root_path, method: :get do %>
  <input onkeydown="if(this.value == '') {this.name = 'query'} else {this.name = 'query'}" value=<%="#{@params[:query]}"%>>
  <%= submit_tag "Search", name:nil %>
  <%end%>


2.ページネーション項目



ページネーション項目は、順序付けられていないリストにラップされます.各アイテムを作成するときに、対応するアイテムに一致するように params ページの値を更新します.残りのパラメータも同様に渡されます. .active クラスは、@current_page に一致する場合、ページネーション項目にスタイルを追加するために使用されます.

  <ul>
    <%[*1..@pages_count].each do |page|%>

    <%@params[:page] = page%>
    <li class=<%="#{@current_page == page ? 'active' : ''}"%>>
      <%= link_to page, root_path(@params)%>
    </li>

    <%end%>
  </ul>


3. 結果項目



すべての結果を繰り返し処理し、ページに表示します.

  <%@results.each do |result|%>

  <div class='results'>
    <%=result%>
  </div>

  <%end%>


これは、最終結果がどのように見えるかです.すべてのコードを div 要素でラップしました.

<div class='demo'>
  <%= form_with url: root_path, method: :get do %>
  <input onkeydown="if(this.value == '') {this.name = 'query'} else {this.name = 'query'}" value=<%="#{@params[:query]}"%>>
  <%= submit_tag "Search", name:nil %>
  <%end%>

  <ul>
    <%[*1..@pages_count].each do |page|%>
    <%@params[:page] = page%>
    <li class=<%="#{@current_page == page ? 'active' : ''}"%>>
      <%= link_to page, root_path(@params)%>
    </li>
    <%end%>
  </ul>

  <%@results.each do |result|%>
  <div class='results'>
    <%=result%>
  </div>
  <%end%>
</div>


このデモで使用されているスタイルは次のとおりです.

.demo {
  margin: 80px auto 0;
  width: 400px;
  display: flex;
  flex-direction: column;
  input:first-of-type {
    width: 200px;
    padding: 5px 10px;
    border-radius: 10px;
    outline: none;
    border: 1px solid black;
  }
  input:last-of-type {
    margin-left: 5px;
  }
  ul {
    display: flex;
    margin-bottom: 20px;
    margin-top: 30px;
    // width: 100%;
    li {
      display: flex;
      justify-content: center;
      border: 1px solid rgb(194, 194, 194);
      font-size: 15px;
      border-radius: 8px;
      padding: 5px 10px;
      margin-right: 20px;
      &.active {
        background: cornflowerblue;
        a {
          color: white;
          font-weight: bold;
        }
      }
    }
  }
}

.results {
  border-radius: 8px;
  box-shadow: 0 0px 2.5px rgba(0, 0, 0, 0.05), 0 0px 20px rgba(0, 0, 0, 0.1);
  font-size: 20px;
  padding: 5px;
  margin-bottom: 5px;
  padding-left: 20px;
}