Fadf50b647fa1fb23c793ad5bd4fb6a3

In a Rails application, I have links that add params to filter or sort data, and I want that when one clicks on a link, the current params are kept.

This is my first attempt.

The way to use it is this:

link_to_param 'Link text', 'param_name', ['value1', 'value2'...]

The values argument is for either one value or several values (like sort flags, so if param_name = value1, then param_name = value2).

So if the query string looks like this:

?sort_by=name_asc

And I do this:

link_to_param 'Sort by name', 'sort_by', ['name_asc', 'name_desc']
link_to_param 'Show only X', 'filter', 'by_x'

Then the generated link's query string will be:

?sort_by=name_desc (first link)
?filter=by_x&sort_by=name_asc (second link)

And when the parameter is not included in the current query string, the first value in the array will be the default.

Is there an easier way to accomplish this?

Comment: Thanks for your refactorings, but I think you didn't quite get what I'm trying to accomplish... Thanks anyway.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def link_to_param(text, param, values)
    @qs ||= {}
    if @qs.blank?
      request.query_string.split('&').each do |p|
        pair = p.split('=')
        @qs[pair[0]] = pair[1]
      end
    end

    qs = []
    values = [values] unless values.is_a?(Array)
    
    value = values[0]
    param_value = values.blank? ? '' : "#{param}=#{values[0]}"

    if request.parameters[param]
      if values.include?(request.parameters[param])
        css_class = 'current'
        
        @qs.each do |p, v|
          value_idx = 0
          value_idx = request.parameters[param] == values[0] ? 1 : 0 if request.parameters[param] == v && values.size > 1
          if p == param
            qs << "#{p}=#{values[value_idx]}"
          else
            qs << "#{p}=#{v}"
          end
        end
      else
        qs << "#{param}=#{value}" unless value.blank?
        @qs.each do |p, v|
          qs << "#{p}=#{v}" unless p == param
        end
      end
    else
      if value.blank?
        css_class = 'current'
      else
        qs << "#{param}=#{value}"
      end
      @qs.each do |p, v|
        qs << "#{p}=#{v}" unless p == param
      end
    end
 
    qs = qs.sort.join('&')
    link_to text, "?#{qs}", :class => css_class
  end

Refactorings

No refactoring yet !

Avatar

lel

July 18, 2008, July 18, 2008 00:34, permalink

No rating. Login to rate!

You should always avoid building url:s or parsing query strings by yourself. It is not the first time it's ever needed so there are ways in the framework to deal with this already. And then the pieces go together very nice resulting in reduced and maintainable code.
Also, if a method exceeds any of the approximate limits of 80 columns, 25 lines or three levels of indentation, you need to stop and maybe reconsider your approach :-)

My example may not be what you want, since I introduced a specific param for the order (to avoid further parsing of the sort_by down the line). But it's a starting point: use params hash to get qs, and use link_to's ability to take the same kind of hash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Example: link_to_toggle_asc_desc("text", {:sort_by => "name", :filter_by => "x"})
# Produces: ?sort_by=name&filter_by=x&order=asc  (order is automatically added)
# Order will auto toggle every time you click the link, and any other query string params
# not overridden by the options you pass in will be preserved.

def link_to_toggle_asc_desc(text, opts={})
  if params[:order] == "desc"
    opts[:order] = "asc"
  else
    opts[:order] = "desc"
  end

  link_to(text, params.merge(opts))
end

# Try this:

<%= link_to_toggle_asc_desc "first", {:sort_by => "name"} %>
<%= link_to_toggle_asc_desc "second", {:filter_by => "x"} %>
5071c5b861341c0dcfcf6ac86327701f

Tien Dung

July 26, 2008, July 26, 2008 03:29, permalink

No rating. Login to rate!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
##
# Fun with if .. else assignment ;)
# Change

  if params[:order] == "desc"
    opts[:order] = "asc"
  else
    opts[:order] = "desc"
  end

# To
# In Ruby, if .. then .. else .. end can return value
  opts[:order] = if params[:order] == "desc" 
    "asc"
  else
    "desc"
  end

# I prefer ternary operator in this case 
  opts[:order] = params[:order] == "desc" ? "asc" : "desc"

Your refactoring





Format Copy from initial code

or Cancel