複数のフィルターでさらに絞り込みたい!その2

フィルター条件に行番号情報を付加する。

paramsの挙動が理解できたところで、フィルター条件に行番号情報を付加してみる。まず、現在フィルター条件が何行あるかという情報を保持しておく必要があると考えた。ここでは余計な手間をかけたくないので、セッションを利用することにした。session[:filter_count]を準備して、そこで保持することにする。そうするとコントローラーのlist、add_filterアクションは以下のようになる。

app/controllers/csvs_controller.rb
コントローラー
  • listアクションは最初にページを読み込んだ時だけ実行される。session[:filter_count] = 0で、初期化する。
  • add_filterアクションの時は、session[:filter_count] += 1で、カウントを1つ増やす。
  • set_paginationの:conditionsの設定で、複数条件を組み合わせたSQLを取得している。
class CsvsController < ApplicationController
...(途中省略)...
  def list
    # 配列の設定
    @date_input_sample = '<span id="date_input_sample">(入力例: 2007/4/1 , 4/1 , 6:10)</span>'
    @column_titles = %w{file_name file_comment editable file_size created_at file_updated_at management_section_id user_id}

    session[:filter_count] = 0
    @filter_column = @column_titles[0]
    
    set_pagination
    render :action => 'list'
  end

  def add_filter
    # 配列の設定
    @date_input_sample = '<span id="date_input_sample">(入力例: 2007/4/1 , 4/1 , 6:10)</span>'
    @column_titles = %w{file_name file_comment editable file_size created_at file_updated_at management_section_id user_id}

    session[:filter_count] += 1
    @filter_column = @column_titles[0]

    render :partial => 'list_filter_condition'
  end

  # paginateを設定するメソッド別に作成して、作業を分担した。
  def set_pagination
    # :conditionsの設定
    filter = params[:filter] || {"0"=>{:column=>@column_titles[0], :item=>"", :id=>20}}
    sqls = []
    filter.each {|c, f| sqls << Filtersql.human_compair_sql(f[:column], f[:item], f[:id])}
    @conditions = sqls.join('AND')
    
    # :orderの設定
    sort_field = params[:sort_field] || 'id'
    sort_field = 'id' if sort_field.empty?
    @next_direction = (params[:sort_direction] == 'asc') ? 'desc' : 'asc'
    @order = "#{sort_field} #{@next_direction}"
    
    @csv_pages, @csvs = paginate :csvs, :per_page => 10, 
                                 :conditions => @conditions, 
                                 :order => @order
  end
...(途中省略)...
app/views/csvs/_list_filter_condition.rhtml
ビュー(「ファイルが...を含む」等の条件の入力フォームを描画)
  • パラメーターを送信するフォーム全てに、以下の設定を追記した。
    • "filter_#{session[:filter_count]}_column"
    • :name => "filter[#{session[:filter_count]}][column]"
<li>
<%# フィルタ列の選択 %>
<% options = @column_titles.map{|t| [Csv.human_attribute_name(t), t]} %>
<%= select_tag "filter_#{session[:filter_count]}_column", 
               options_for_select(options, @filter_column), 
               :name => "filter[#{session[:filter_count]}][column]" %><%# フィルタ列を選択するselect_tag :filter_columnを監視して、変化したら、再描画する。 %>
<%= observe_field "filter_#{session[:filter_count]}_column", 
                  :update => "filter_update", 
                  :submit => "filter_params", 
                  :url => {:action => 'change_filter_column'} %>

<%# フィルタ文字と比較方法を入力 %>
<%# 列タイトルごとに部分テンプレートを用意 _filter_フィールド名.rhtml %>
<%= render :partial => "filter_#{@filter_column}" %>
</li>

問題発生

途中までやって、同時に設定しておくべきことが、他にもまだあることに気付く...。

      • パラメーターの送信範囲
      • ajax更新する範囲

フィルターに関する操作やイベントで、どのようなアクションが必要か。そして、そのアクションに必要な送信パラメーターと更新範囲を、決定しておく必要がある。表にまとめてみた。

操作、イベント メソッド 送信パラメーター、id属性 更新範囲、id属性
最初のリスト表示 list すべて 全体
フィルターを実行 list_update すべて、id="list_params" ファイルリストのテーブルだけ更新、id="list_update"
フィルター条件を追加 add_filter なし フィルター条件の最終行に追加、id="filters"
フィルター列の選択 change_filter_column 選択したフィルター列名称と行番号、id="filter_params#{行番号}" _list_filter_condition.rhtmlが描画する部分、id="filter_update#{行番号}"
  • 特に、フィルター列の選択操作では、注意が必要だ。入力フォームを再描画する時に:name => "filter[#{session[:filter_count]}][column]"とやってしまうと、行番号の設定がマズい。(常に最終行の行番号が設定されてしまう...。)選択したフィルター条件の行番号を設定する必要がある。
id属性の構成図

list.rhtml
<div id="list_params">

_list_filter.rhtml(フィルターの実行/解除/追加/削除という操作リンクの行)
<span id="filters">

_list_filter_condition.rhtml(フィルター条件の行)

<span id="filter_params">
フィルター列のプルダウンリスト
</span>

<span id="filter_update">

_filter_列タイトル名.rhtml(列タイトル別の入力フォーム)

</span>

</span>
フィルター条件を追加のリンク
フィルターを実行/解除のリンク

<div id="list_update">
<table>リスト表示する部分

_listh.rhtml(列タイトル)

_listd.rthml(リストデータ)

</table>

前ページ/次ページのリンク
CSVファイルを新規登録のリンク
</div>
</div>