列タイトルをクリックして並べ替えたい!
一覧表示のページで、TitleやCreated_on、Updated_on等々の列タイトルをクリックしたら、その列の順番で一覧表全体を並べ替えできるようにしたい。
クリックした列で並び替えを実行する。
並べ替えをするためには、paginateのオプションに「:order => フィールド名」を追加すれば、指定したフィールドの順番で並べ替えが出来るようだ。キーワード絞り込みのコードを参考に、以下のように作業してみた。
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= link_to column.human_name, :action => 'list', :order_field => column.name unless column.name == 'url' %></th> <% end %> <th><%= link_to Keyword, :action => 'list', :order_field => 'keyword_id' %></th> </tr> ...(以下省略)...
- コントローラ softwares_controller.rb
def list @keyword_id = params[:keyword_id] @order_field = params[:order_field] conditions = ["keyword_id = ?", @keyword_id] unless @keyword_id == nil order = @order_field unless @order_field == nil @software_pages, @softwares = paginate :softwares, :per_page => 10, :conditions => conditions, :order => order end
列タイトルにはリンクが設定され、クリックすれば並び替えが実行できるようになった。
逆順の並び替えもしたい!
今の機能は、クリックした列を対象に昇順に並び替えるだけ。並び替えには降順も必要だ。同じ列のクリックを繰り返せば、昇順と降順が切り替わるようにしたい。ややこしそうだが、以下のようにやってみた。
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= link_to column.human_name, :action => 'list', :order_field => column.name, :order_direction => @order_next_direction unless column.name == 'url' %></th> <% end %> <th><%= link_to Keyword, :action => 'list', :order_field => 'keyword_id' :order_direction => @order_next_direction %></th> </tr> ...(以下省略)...
- 並べ替え方向のパラメータ:order_directionを追加した。
- コントローラ softwares_controller.rb
def list @keyword_id = params[:keyword_id] @order_field = params[:order_field] @order_direction = params[:order_direction] if params[:order_field] == flash[:order_field]......(1) @order_next_direction = (@order_direction == nil) ? 'desc' : nil......(2) flash[:order_field] = @order_field......(3) conditions = ["keyword_id = ?", @keyword_id] unless @keyword_id == nil order = "#{@order_field} #{@order_direction}" unless @order_field == nil......(4) @software_pages, @softwares = paginate :softwares, :per_page => 10, :conditions => conditions, :order => order end
- (1)で、同じ列タイトルのクリックを繰り返している場合は、パラメータを読み取って変数@order_directionに設定するようにした。
- 同じ列タイトルのクリックでない場合は、常に昇順の並べ替えで始まるようにしたいための処理。
- 今クリックした列params[:order_field]と、その前にクリックした列flash[:order_field]を比較して、同じ列のクリックを繰り返しているかどうかを判断している。
- (2)で、逆方向の並び替え方向を変数@order_next_directionに設定している。
- 並び替えの方向は、昇順なら何も設定せずnil、降順ならdescの文字列を設定している。
- paginateで、order => 'title desc'のように設定すると、titleフィールドを降順に並べ替える指定になる。並べ替え方向を指定しないorder => 'title'なら昇順になる。
- (3)で、現在クリックした列タイトルを、flash[:order_field]に保存している。
- (4)で、@order_fieldと@order_directionから並べ替えの条件を作成している。
ずいぶん分かりづらくなったが、これでなんとか昇順、降順の切り替えも可能になった。
昇順、降順のマークを付ける。
並び替えの方向を示すマークを列タイトルに付けることにした。昇順なら▲、降順なら▼を使ってみる。例えば、タイトルで並びかえたら、「▲Title」のように表示するつもり。
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= link_to "#{@direction_mark if @order_field==column.name}#{column.human_name}", :action => 'list', :order_field => column.name, :order_direction => @order_next_direction unless column.name == 'url' %></th> <% end %> <th><%= link_to "#{@direction_mark if @order_field=='keyword_id'}Keyword", :action => 'list', :order_field => 'keyword_id' :order_direction => @order_next_direction %></th> </tr> ...(以下省略)...
- @order_fieldと列タイトルが一致した時に、タイトルの頭に方向マークが表示される。
- コントローラ softwares_controller.rb
def list @keyword_id = params[:keyword_id] @order_field = params[:order_field] @order_direction = params[:order_direction] if params[:order_field] == flash[:order_field] @order_next_direction = (@order_direction == nil) ? 'desc' : nil @direction_mark = @order_direction == nil ? '▲' : '▼' flash[:order_field] = @order_field conditions = ["keyword_id = ?", @keyword_id] unless @keyword_id == nil order = "#{@order_field} #{@order_direction}" unless @order_field == nil @software_pages, @softwares = paginate :softwares, :per_page => 10, :conditions => conditions, :order => order end
並べ替えとキーワード絞り込みの連動
最初に比べると、ずいぶん進化した感じだが、まだ一つやっておきたいことがある。それは、並び替えとキーワード絞り込みを連動して操作できるようにすること。例えば、キーワードRuby on Railsで絞り込みしている状態で、Title順に並び替えを実行できるようにしたい。(現状では、Title順に並び替えを実行すると、キーワード絞り込みは解除されてしまう)
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= link_to "#{@direction_mark if @order_field==column.name}#{column.human_name}", :action => 'list', :keyword_id => @keyword_id, :order_field => column.name, :order_direction => @order_next_direction unless column.name == 'url' %></th> <% end %> <th><%= link_to "#{@direction_mark if @order_field=='keyword_id'}Keyword", :action => 'list', :keyword_id => @keyword_id, :order_field => 'keyword_id' :order_direction => @order_next_direction %></th> </tr> ...(途中省略)... <td><%= link_to h(software.keyword.name), :action => 'list', :keyword_id => software.keyword_id, :order_field => @order_field, :order_direction => @order_direction unless software.keyword_id == nil %></td> ...(途中省略)... <%= link_to 'New software', :action => 'new' %> <%= ' |' unless @keyword_id == nil && @order_field == nil %> <%= link_to 'Back', :action => 'list', :id => nil unless @keyword_id == nil && @order_field == nil %>
- 連動させるために、並び替えと絞り込み両方のパラメータを、常に設定するようにした。
- 並び替えや絞り込みをしている時はBackリンクを表示して、クリックすれば全ての条件を解除する。
これで、条件を連動して操作できるようになった。
キーワードだけの絞り込みと解除
並べ替えと絞り込みが連動して気になってきたのが、キーワード絞り込みの解除。今はBackリンクで解除すると、並べ替えまで解除してしまう。キーワード絞り込みだけ解除する方法が必要だ。昇順降順の切り替えと同じように、同じキーワードを繰り返しクリックすると、絞り込みとその解除をするようにしてみた。
- コントローラ softwares_controller.rb
def list @keyword_id = params[:keyword_id] unless params[:keyword_id] == flash[:keyword_id] flash[:keyword_id] = @keyword_id @order_field = params[:order_field] @order_direction = params[:order_direction] if params[:order_field] == flash[:order_field] @order_next_direction = (@order_direction == nil) ? 'desc' : nil @direction_mark = @order_direction == nil ? '▲' : '▼' flash[:order_field] = @order_field conditions = ["keyword_id = ?", @keyword_id] unless @keyword_id == nil order = "#{@order_field} #{@order_direction}" unless @order_field == nil @software_pages, @softwares = paginate :softwares, :per_page => 10, :conditions => conditions, :order => order end def list_by_keyword list render :action => 'list' end def list_in_order @keyword_id = params[:keyword_id] # キーワード絞り込み維持のため list render :action => 'list' end
- ビュー list.rhtml
<h1>Listing softwares</h1> <table> <tr> <% for column in Software.content_columns %> <th><%= link_to "#{@direction_mark if @order_field==column.name}#{column.human_name}", :action => 'list_in_order', :keyword_id => @keyword_id, :order_field => column.name, :order_direction => @order_next_direction unless column.name == 'url' %></th> <% end %> <th><%= link_to "#{@direction_mark if @order_field=='keyword_id'}Keyword", :action => 'list_in_order', :keyword_id => @keyword_id, :order_field => 'keyword_id' :order_direction => @order_next_direction %></th> </tr> ...(途中省略)... <td><%= link_to h(software.keyword.name), :action => 'list_by_keyword', :keyword_id => software.keyword_id, :order_field => @order_field, :order_direction => @order_direction unless software.keyword_id == nil %></td> ...(途中省略)... <%= link_to 'New software', :action => 'new' %> <%= ' |' unless @keyword_id == nil && @order_field == nil %> <%= link_to 'Back', :action => 'list', :id => nil unless @keyword_id == nil && @order_field == nil %>
なんとか目的の機能は実現できたが、非常に分かりづらいコードになってしまった...。おそらく、並び替えとか、絞り込みは定番の機能だから、もっと分かり易い定石のような書き方があるのだと思う。今後は検索との連動にも対応したいので、もう少しシンプルに表現できるように勉強が必要だ。
- 同じ列タイトルのクリックを繰り返すと、昇順、降順を切り替えて並べ替えが行われる。(最初のクリックは常に昇順)
- 同じキーワードのクリックを繰り返すと、絞り込みと、その解除が交互に行われる。
- 並び替えと、キーワード絞り込みが連動する。並び替え状態を維持しながら絞り込み、絞り込み状態を維持しながらの並び替えが可能。
- 並び替え、または絞り込みの状態ではBackリンクを表示して、クリックすると条件を全て解除する。