31

I have a logic where I allow sorting on price and relevance. I am doing this by passing parameters to controller. My URL has a parameter - 'sort' which can have a value - 'price_lowest' or 'default'. The links looks like:

<a href="<%= request.fullpath + '&sort=price_lowest' %>">lowest prices</a> | 
<a href="<%= request.fullpath + '&sort=default' %>">relevance</a>

problem with the above code is that it "adds" parameters and does not "replace" them. I want to replace the value of &sort= parameter without adding a new value. E.g. I don't want :

../&sort=price_lowest&sort=price_lowest&sort=default

With the current logic - I am getting the above behaviour. Any suggestions ?

1
  • 4
    you should really avoid this kind of hacks. Named routes are made to answer your needs Commented Jul 8, 2011 at 13:43

5 Answers 5

68

In order to preserve the params I did this:

<%= link_to 'Date', params.merge(sort: "end_date") %>

However the url will be ugly.

UPDATE

For Rails 5 use:

<%= link_to 'Date', request.params.merge(sort: "end_date") %>
Sign up to request clarification or add additional context in comments.

3 Comments

why not <%= link_to 'Date', params.merge(:sort => "end_date") %> btw your name looks interesting , like Chinese...
In rails 5 you need to use request.params <%= link_to 'Date', request.params.merge(:sort => "end_date") %>
For Rails 5, github.com/rails/rails/issues/26289 is a good read, with an explanation of the risks and examples of solutions.
33

If you only need one cgi param and want to stay on the same page, this is very simple to achieve:

<%= link_to "lowest prices", :sort => "price_lowest" %>

However, if you have more than one, you need some logic to keep old ones. It'd probably be best extracted to a helper, but essentially you could do something like this to keep the other params..

<%= link_to "lowest prices", :sort => "price_lowest", :other_param => params[:other] %>

Named routes will only really help you here if you need to go to another page.

9 Comments

This removes the existing parameters. I end up with - localhost:3000/view?sort=price_lowest - all other previous parameters are lost.
Can you elaborate your solution a bit ?
As I said in the answer, if you have more than the one param, you'll need to have some logic to keep the ones you're not setting. A helper is definitely the best place for something like this. Here's a (potentially flawed, poorly written and untested) example of how to achieve that: raw.github.com/gist/e22343babcb76381304c/…
And if you only need the url you can use url_for params.merge( sort: 'price_lowest' )
Does not work, loses all other params
|
13

If a path is not passed to the link_to method, the current params are assumed. In Rails 3.2, this is the most elegant method for adding or modifying parameters in a URL:

<%= link_to 'lowest prices', params.merge(sort: 'end_date') %>
<%= link_to 'relevance', params.merge(sort: 'default') %>

params is a Ruby hash. Using merge will either add a key or replace the value of a key. If you pass nil as the value of a key, it will remove that key/value pair from the hash.

<%= link_to 'relevance', params.merge(sort: nil) %>

Cite:

2 Comments

Combined with the active_link_to Gem and you'll also have nice active classes on your filters. - github.com/comfy/active_link_to
You should update your answer for strong parameters and using params.permit! in your controller.
1

My working solution on Rails 3.1 of course, it's hardcode, and has to be refactored.

item model

  def self.get(field,value)
    where(field=>value)
  end

items controller

@items=Item.all
 if params[:enabled]
  @[email protected](:enabled, params[:enabled])
end
if params[:section]
  @[email protected](:section_id, params[:section])
end

items helper

def filter_link(text, filters={}, html_options={})
  trigger=0
  params_to_keep = [:section, :enabled]
  params_to_keep.each do |param|
    if filters[param].to_s==params[param] && filters[param].to_s!="clear" || filters[param].to_s=="clear"&&params[param].nil?
      trigger=1
    end
    if filters[param]=="clear"
      filters.delete(param)
    else
      filters[param]=params[param] if filters[param].nil?
    end
  end
  html_options[:class]= 'current' if trigger==1
  link_to text, filters, html_options
end

items index.html.erb

<%= filter_link 'All sections',{:section=>"clear"} %>
<% @sections.each do |section| %>
   <%= filter_link section.title, {:section => section} %>
<% end %>

<%= filter_link "All items", {:enabled=>"clear"} %>
<%= filter_link "In stock", :enabled=>true %>
<%= filter_link "Not in stock", :enabled=>false %>

Comments

0

It's not quite the answer to the question that you're asking, but have you thought about using the Sorted gem to handle your sorting logic and view links?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.