複数のフィルターでさらに絞り込みたい!
csv_serverのファイル件数が増えてくれば、ユーザーからは、すぐにこんな要望の連絡が入りそうだ。「編集可能な経理部の利益管理表リストが見たい!」とか、「2007年の人事部のファイルで、あとで修正が入ったリストが見たい!」など。しかし、今の状態では1つの条件でしかフィルターできないので、「並び替えでも使って自分の目で探してください。」としか答えようが無い...。
考えてみれば、googleだって、複数条件や絞り込み検索ができなかったら誰も使わないと思う。求められているのは、自由に条件を組み合わせることが出来るフィルターなのです。シングルフィルターのままでは、高性能なSQLの検索機能も無駄になってしまう...。でも、マルチフィルターに変更するのは、ちょっと手間がかかりそうだ。まず、どのような機能にするか決めなくてはならない。
目指す所
そこで、とりあえず身近なところでお手本にしたのが、MacOSX10.4 TigerにインストールされているMailの「振り分けルール」を作成する時の条件フィルター。見た目はこんな感じのインターフェースになっている。
- 最初はシンプルだけども、必要なフィルター条件の追加、削除が簡単にでき、複雑な条件を作成することも可能。
- 入力フォームは、フィルター項目に合わせて最適なインターフェースに変化する。
昔よくあった、検索用の専用ページを用意して、全てのフィルター項目をはじめから羅列するようなインターフェースは嫌いだ。(項目があり過ぎて、どこに何を入力すればいいか、まったく分かり難いのだ...。)
フィルター条件を追加できるようにする。
やるべきことがあり過ぎて、何から手を付けて良いのか分からない。そんな時、自分では見た目(インターフェース)を最初に作り始める。まずは「フィルター条件を追加」のリンクを作成して、条件の入力フォームを追加できるようにしてみた。
- 現状、フィルター条件と、フィルター実行/解除の操作が1行になってしまっている。条件の入力行と、実行/解除の操作行を分けた。画面の構成は以下のような感じになる。
- フィルター条件を追加リンクをクリックすると、<span id="filters">タグの部分に、新たなフィルター条件が追加されるようにする予定。
list.rhtml
_list_filter.rhtml(フィルターの実行/解除/追加/削除という操作リンクの行)
<span id="filters">_list_filter_condition.rhtml(フィルター条件の行)
</span>
フィルター条件を追加のリンク
フィルターを実行/解除のリンク<table>リスト表示する部分
_listh.rhtml(列タイトル)
_listd.rthml(リストデータ)
</table>
前ページ/次ページのリンク
CSVファイルを新規登録のリンク
コーディング
- app/views/csvs/_list_filter.rhtml
- ビュー(「フィルター条件を追加」のリンクを描画)
- link_to_remoteのオプション:position => :bottomを指定することで、<span id="filters">タグの一番下に、リンクをクリックする度に追加される。(:position => :bottomがないと、追加でなく、上書き更新になってしまい、フィルター条件は増えない。)
- 以下は:positionの指定による挿入位置の違い。
-
- :position => :beforeで挿入される位置(タグの直前)
- <span id="filters">
- :position => :topで挿入される位置(タグ内の一番上)
- :position => :bottomで挿入される位置(タグ内の一番下)
- </span>
- :position => :afterで挿入される位置(タグの直後)
-
...(途中省略)... <!--フィルター条件の行--> <ol> <span id="filters"> <%# :position => :bottomによって、ここにフィルター条件が追加されていく。 %> <%#= render :partial => 'list_filter_condition' %> </span> <small> <%= link_to_remote 'フィルター条件を追加', :update => 'filters', :submit => 'filters', :url => {:action => 'add_filter'}, :position => :bottom %> </small> </ol> ...(途中省略)...
- app/controllers/csvs_controller.rb
- コントローラー
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} @filter_column = @column_titles[0] render :partial => 'list_filter_condition' end
- app/views/csvs/_list_filter_condition.rhtml
- ビュー(「ファイルが...を含む」等の条件の入力フォームを描画)
<li> <%# フィルタ列の選択 %> <% options = @column_titles.map{|t| [Csv.human_attribute_name(t), t]} %> <%= select_tag "filter_column", options_for_select(options, @filter_column) %>が <%# フィルタ列を選択するselect_tag :filter_columnを監視して、変化したら、再描画する。 %> <%= observe_field "filter_column", :update => "filter_update", :submit => "filter_params", :url => {:action => 'change_filter_column'} %> <%# フィルタ文字と比較方法を入力 %> <%# 列タイトルごとに部分テンプレートを用意 _filter_フィールド名.rhtml %> <%= render :partial => "filter_#{@filter_column}" %> </li>
問題点
以上で、フィルター条件は追加される事はされるが、見た目だけ...。条件が複数あっても、フィルター条件として機能するのは最初の条件だけの状態。それにフィルターを実行してしまうと、今まで追加した条件は消えてしまう...。なぜか?
- フィルター条件が複数あっても、パラメーターとして送信される時は、常に:filter_column, :filter_item, :filter_idというparams[]の識別子で送信されている。(例えば、2つの条件なら、params[:filter_column]が2重に送信され、取得できるのはおそらく1行目の値だけになってしまうのだろう。)パラメーターにフィルター条件の行番号の情報を付加する必要がある。
- シングルフィルターでやっていた時は、ajaxと言いながら、フィルターを実行する度にページ全体を更新していた。これだと、部分更新で追加した条件は、消えてしまうのは当たり前か...。フィルターを実行した時にCSVファイルリストの部分だけに更新範囲を限定する必要がある。