複合条件でリスト表示、実際のコード。

以下は、Ajaxを利用して、並び替え、キーワードフィルター、フリーワード検索の複合条件で、リスト表示するための実際のコード。

app/controllers/softwares_controller.rb

  • Ajax処理の場合は、list_updateが処理する。その際、@ajax_update変数にtrueを設定する。(list.rhtmlでAjax処理かどうかを判断して、描画内容を変更するため。)
  • conditionsの設定にはSQLLIKE演算子を利用した。
    • %は、0文字以上の任意の文字。_は、任意の一文字。
    • %や_を検索したい場合は、ESCAPE 句でエスケープ文字を指定する。エスケープ文字の次の1字は、通常の文字扱いになる。
class SoftwaresController < ApplicationController
  include SoftwaresHelper

  hide_field :id, :url, :user_id, :only => [:index, :list_update, :list, :list_by_keyword, :list_in_order]
  hide_field :id, :created_on, :updated_on, :user_id, :only => [:new, :confirm_create, :create, :edit, :onfirm_update, :update]
...(途中省略)...
  def set_pagination
    #パラメーターを取得
    @keyword_id = params[:keyword_id] || params[:store_keyword_id] || "%"
    @order_field = params[:order_field] || params[:store_order_field] || "id"
    @order_direction = params[:order_direction] || params[:store_order_direction] || "ASC"
    @search_word = params[:store_search_word]
    
    #条件によって、パラメーターを変更
    @keyword_id = "%" if params[:keyword_id] == params[:store_keyword_id]
    @order_direction = 'ASC' if params[:order_field] && !(params[:order_field] == params[:store_order_field])
    @direction_mark = @order_direction == 'DESC' ? '' : ''
    @order_next_direction = @order_direction == 'DESC' ? 'ASC' : 'DESC'
    
    #paginate処理
    conditions = ["keyword_id LIKE ? AND (title LIKE ? OR description LIKE ?)", 
                  @keyword_id ,"%#{@search_word}%" ,"%#{@search_word}%" ]
    order = "#{@order_field} #{@order_direction}"
    @software_pages, @softwares = paginate :softwares, :per_page => 4,
                                           :conditions => conditions,
                                           :order => order
  end

  def list_update
  Software.with_scope(login_user) do
    @ajax_update = true # Ajax処理であることを示す。
    set_pagination
    # :layout => falseで、レイアウトテンプレートsoftwares.rhtmlを利用しないで描画する。
    render :action => "list", :layout => false
  end
  end
  
  def list
  Software.with_scope(login_user) do
    set_pagination
  end
  end
...(途中省略)...

app/views/softwares/list.rhtml

<%# listメソッドから描画する場合は、Ajaxで更新する部分list_updateを<div>タグで指定でする。 %>
<% unless @ajax_update %>
<div id="list_update">
<% end %>

<%# list_updateメソッドから描画する場合は、スタイルシートを適用する。 %>
<% if @ajax_update %>
  <head>
    <%= stylesheet_link_tag 'scaffold' %>
  </head>
<% end %>

<h1><%= localize(:model, :software) %><%= localize(:title, :list) %></h1>

<div class="indent">
<%= localized_error_messages_for 'software' %>

<table class="confirm">

<%# リストをフォーム、ヘッダー、データ部分に分けて描画する。 %>
<%= render :partial => 'models/softwares/list_form' %>
<%= render :partial => 'models/softwares/listh' %>  
<%= render :partial => 'models/softwares/listd', :collection => @softwares %>

<%# link_to_remoteによる、前ページ<<  >>次ページ %>
<tr><th colspan="6">
<%= link_to_remote "#{localize(:label, 'Previous page')} << ", 
              :update => "list_update", 
              :submit => "store", 
              :url => { :action => 'list_update', 
                        :page => @software_pages.current.previous } if @software_pages.current.previous %>   
<%# pagination_linksについては、Ajaxに対応させることが出来なかったので、とりあえずコメントアウト。 %>
<%#= pagination_links @software_pages %>
<%= link_to_remote " >> #{localize(:label, 'Next page')}", 
              :update => "list_update", 
              :submit => "store", 
              :url => { :action => 'list_update', 
                        :page => @software_pages.current.next } if @software_pages.current.next %>
</th></tr>

</table>
<br />
<%= link_to localize(:label, :new), :action => 'new' %>
</div>

<%# listメソッドから描画する場合の、終了タグ。2-4行目に対応。 %>
<% unless @ajax_update %>
</div>
<% end %>

app/views/models/softwares/_list_form.rhtml

<%# フォームを送信するブロックを指定 %>
<div id="store">

  <%# returnキーでフォームをAjax送信する。 %>
  <%= form_remote_tag :update => "list_update", 
                      :submit => "store", 
                      :url => {:action => 'list_update'} %>

    <%= link_to 'リセット', :action => 'list' unless @keyword_id.nil? && @order_field.empty? %>  
    
    <span style="font-weight:bold;">
    <%= human_attribute_name(Software, "title") %>/<%= human_attribute_name(Software, "description") %>
    </span>を検索 :
    <%= text_field_tag("store_search_word", @search_word) %>

    <%# 「検索」ボタン、フォームをAjax送信する。 %>
    <%= submit_to_remote "store", "検索", 
              :update => "list_update", 
              :submit => "store", 
              :url => {:action => 'list_update'} %>

    <%# 「クリア」リンク、フォームをAjax送信する。 %>
    <small>
    <%= link_to_remote "クリア", 
              :update => "list_update", 
              :submit => "store", 
              :url => {:action => 'list_update', :store_search_word => ""} %>
    </small>

    <%# 設定値を保持するためのフォーム %>
    <%= hidden_field_tag("store_order_field", @order_field) %>
    <%= hidden_field_tag("store_order_direction", @order_direction) %>
    <%= hidden_field_tag("store_keyword_id", @keyword_id) %>
  <%= end_form_tag %>
</div>

app/views/models/softwares/_listh.rhtml

<!--[:]-->
<tr>
  <% for column in Software.columns %>
    <% unless @controller.class.hidden_field?(@action_name, column.name) %>
      <%# 列タイトルをクリックした時の並べ替えをAjaxで処理する。 %>
      <th><%= link_to_remote "#{@direction_mark if @order_field==column.name}#{human_attribute_name(Software, column.name)}", 
                :update => "list_update", 
                :submit => "store", 
                :url => {:action => 'list_update', 
                         :order_field => column.name, 
                         :order_direction => @order_next_direction} %></th>
    <% end %>
  <% end %>
  <th><br></th>
</tr>
<!--[:]-->

app/views/models/softwares/_listd.rhtml

...(途中省略)...
      <%- unless @controller.class.hidden_field?(@action_name, 'keyword_id') -%>
        <%# キーワードのフィルタリングをAjaxで処理する。 %>
        <td width="16%"><%= link_to_remote h(listd.keyword.name), 
                              :update => "list_update", 
                              :submit => "store", 
                              :url => {:action => 'list_update', 
                                       :keyword_id => listd.keyword_id} %></td>
      <%- end -%>
...(途中省略)...


現在、pagination_linksをAjax処理に対応できない状態だ。その方法はHow to Paginate With Ajaxがとても参考になりそう。複合条件を保持するためには、ページ番号をクリックした時にも、link_to_remoteのようにフォームデータを送信する必要がある。そうだ、pagination_links_remoteが欲しい!