The default URL generated after a get request in Rails isn't very nice to look at.
Using the meta_search gem in a Rails 3 application, I've created a select menu to filter a list of articles by category. The code looks like this:
<%= form_for #search, :url => articles_path, :html => {:method => :get} do |f| %>
<%= f.label :category_name_equals, "Filter by category:" %>
<%= f.collection_select :category_name_equals,
Category.all(:order => "name"), :name, :name, :include_blank => true %>
<%= f.submit "Search" %>
<% end %>
<table>
<tr>
<th>Title</th>
<th>Category</th>
</tr>
<% for article in #articles %>
<tr>
<td><%= link_to article.title, article %></td>
<td><%= article.category.name %></td>
</tr>
<% end %>
</table>
Right now, when the form is submitted, a lengthy URL is returned containing the params hash:
http://localhost:3000/articles?utf8=%E2%9C%93&search[category_name_equals]=
Reviews&commit=Search
I want to keep the URL as clean as possible, for example:
http://localhost:3000/articles/category/reviews
or
http://localhost:3000/articles?category=reviews
What are some solutions to make the params hash URL more readable by humans?
You can't do that with a form with get method. it's an HTML issue.
You need add some Javascript to convert your URL to pretty URL or you can get URL in your controller in your rails application and redirect to your pretty URL.
Like shingara said, don't use a form at all. Instead use a list of links, and fancy things up with some css/js that makes your list behave/look like a drop-down menu:
<ul>
<% Category.all.each do |c| %>
<li>
<%= link_to(c.name, :controller => :articles, :action => :browse, :category => c.tag) %>
</li>
<% end %>
</ul>
For your routes, you'll want something like this, but DRYer:
routes.connect '/articles/browse/:category', :controller => :articles, :action => :browse
You can find many examples of CSS/JS dropdowns very easily: http://bit.ly/dRtQNd
You can find examples of good CSS/JS dropdown menus with considerably more effort ... unless you give up just ask for advice on stack overflow again :-). (Coding rudimentary versions of standard UI components from scratch is simple these days using jQuery and whatnot, so lots of people do it. Coding ones that "feel right" is actually very hard; fewer people do that!)
P.S. That form_for #search looks utterly weird. This is not what form_for #your_model_object was meant for...
Related
Here is my issue:
I am setting up internationalization on my site (so we can have multiple translations of the text on the pages). I have followed a rails cast to set up a page that can manage the translations instead of me manually having to edit every yml file.
I have set everything up and can create entries fine, I am trying to add the ability to delete an entry and I have hit a wall. I can't seem to set up the link correctly to delete the entry from redis. The first thing that made this complicated (at least to me) is that I am not deleting an object that was created through active record (like a user etc). So instead of using the active record object to construct the url for the link_to or form_for I have to construct it manually.
From what I have read so far I have to put the link in a form (and set to post since we are modifying the redis db). So I have been trying to create the correct syntax in the form for tag to direct to the action I have set up in the controller.
Controller:
class InternationalizationTranslationsController < ApplicationController
def index
#translations = I18n.backend.store
end
def create
I18n.backend.store_translations(params[:locale], {params[:key] => params[:value]}, :escape =>false)
redirect_to internationalization_translations_url, :notice => "Added translation"
end
def destroy
puts "Key is: #{params[:key]}"
I18n.backend.delete(params[:key])
redirect_to internationalization_translations_url, :notice => "Removed translation"
end
end
View:
<%= form_tag internationalization_translations_path do %>
<p>
<%= label_tag :locale %>
<%= text_field_tag :locale %>
</p>
<p>
<%= label_tag :key %>
<%= text_field_tag :key %>
</p>
<p>
<%= label_tag :value %>
<%= text_field_tag :value %>
</p>
<p><%= submit_tag "Submit" %></p>
<% end %>
</div>
<div class="grid_7 top_padding">
<table class="trans_table">
<% #translations.keys.each_with_index do |key, i| %>
<tr class="<%= i%2 == 0 ? "even" : "odd" %>">
<td><%= key %></td>
<td><%= #translations[key] %></td>
Then I played with form_for and form_tag looking at the documentation (form helpers and form tag docs) eventually ending with these, that still do not work:
<%= form_tag(controller: "internationalization_translations", action: "destroy", method: "post", key: key) %>
<%= submit "Delete" %>
<% end %>
and now
<%= form_tag(internationalization_translations_path, action: "destroy", method: "post", key: key) do %>
<%= submit_tag "Delete" %>
<% end %>
I also played with the link_to for a while before coming across this post which linked to why the delete link/button should be in a form because it is editing the DB so it needed to be post instead of get. I am a little frustrated because this seems like a pretty straight forward task but I am running into some difficulties finding a clear answer regarding my particular issue, specifically the routing for this link for a redis entry and not an activerecord object.
**also since the form for the button is being created in a loop for each entry I should probably have the form named with an index so it is specific for each button?
Any insight or links would be greatly appreciated.
Thanks,
Alan
Ok so I ended up figuring it out resetting some things up and taking marvwhere's advice. I wanted to set it up as a link without a form, how it generated for other controllers that were manipulating active record objects. But since this was a different case making a custom action other than the default destroy action worked.
<%= form_tag(destroy_key_internationalization_translations_path, method: :post) do %>
<%= hidden_field_tag 'key', key %>
<%= submit_tag "Delete" %>
<% end %>
where I created the destroy_key action within the internationalization_translation controller.
Also the deleting of the key from redis needed to be changed. I had to use the actual Redis instance created. So instead of
I18n.backend.delete(params[:key])
in my initializer I had to set a global variable when creating the Redis instance:
TRANSLATION_STORE = Redis.new(:db => 10)
and then call the delete on that object
TRANSLATION_STORE.del(params[:key])
In my rails application I wanted to use pagination for names. I'm fetching the names from a postgresql table.
def index
#users = User.order("name").paginate(:page=>params[:page],:per_page=>50)
end
Is there a way of alphabetic pagination that has page numbers as a,b,c..........z
Thanks for your help,
Ramya.
Im doing this too in one of my projects. I didnt use a gem for this. Its really easy using Ranges... For example:
<% ('A'..'Z').each do |char| %>
<% if char==params[:char] %>
<%= link_to :action => 'your_action', :char => char, :class => 'selected_char' %>
<% else %>
<%= link_to :action => 'your_action', :char => char %>
<% end %>
<% end %>
Then in the controller Action you select the Objects by the params[:char] given. For example:
#instance_var = ModelName.where("field LIKE ?", "#{params[:char]}%")
this did it for me. You can easy build a partial out of this and use it for many different Models.
Using rails 3.1, ruby 1.92. I have a bunch of products in a store: movies, music, and books. I have three scopes in my products controller, all called the same (movies, music, and books). The point of our assignment is to have the index of the product controller show all items on load of the site. No problem. We then have to link_to books, music, or movies, and have the appropriate products shown. Also done.
However, I now have three files in my views, and I need to make a partial to display all of these. I have used this:
<% #products.each do |product| %>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%= product.title %></h3>
<%= sanitize(product.description) %>
<div class="price_line">
<span class="price"><%= number_to_currency(product.price) %></span>
<%= button_to 'Add to Cart', line_items_path(:product_id => product) %>
</div>
</div>
<% end %>
I don't really understand what is happening. I have taken what is originally a '#product' and with the use of my scope, it is now #book, or #music, etc. In my code with the three files, I have this code, but instead of '#products' and 'do |product|', I have #books do |book| etc.
What do I need to do so the partial will be able to be functional using #books, #music, and #movies? Each time I pass these variables I get the NilClass error. How do I go about this?
Check out the rails guide on layouts and rendering - especially the section on partials, which I linked you directly to.
Basically, you want to refactor the code to avoid using #products, #books, #movies, etc, and make a partial that uses a local variable.
In your products view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #products} %>
In your books view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #books} %>
In your movies view:
<%= render :partial => "whatever_you_name_it", :locals => {:products => #movies} %>
And inside the partial, just change your #products.each do |product| to products.each do |product|
If I understand well your problem you need something like :
render :partial => 'partial_name', locals => {:items => #books}
And, in your partial, you can do:
items.each do |item| ...
I have a list of 'notes', and each note has some tags via acts_as_taggable_on. It's a great plugin, and the tags are working wonderfully.
What would be the best way to filter this list of notes by the tag that is clicked on?
Example:
<% #notes.each do |note| %>
<%= note.content %>
<% note.tag_list.each do |tag| %>
<%= link_to tag, '#', :class => "tag" %>
<% end %>
<% end %>
What should I replace the '#' with in order to change or scope out #notes? Not too familiar with this.
EDIt: I want something just like StackOverFlow actually, how would I add parameters to the URL based on the link?
Thanks!
I believe I figured it out. Was just having an off-moment.
I can create a named route like:
match 'tags/:tag' => 'controller#index', :as => 'tag'
And that way I can get the parameter I need.
I've set up a Rails form roughly following the instructions in this Railscast.
Here's the code for the form:
<% form_tag complete_todos_path, :method => :put do %>
<ul>
<div id="incomplete_todos">
<% #incomplete_todos.each do |todo| %>
<%= render :partial => todo %>
<% end %>
</div>
</ul>
<%= submit_tag "Mark as completed" %>
<% end %>
And here's the code for the todo partial:
<div class="todo">
<li>
<%= check_box_tag "todo_ids[]", todo.id %>
<%=h todo.name %>
<%= link_to 'edit', edit_todo_path(todo) %>
<%= link_to 'delete', todo, :confirm => 'Are you sure?', :method => :delete %>
</li>
</div>
It's working great, but I'm looking to start implementing AJAX and I need each checkbox to have a unique id. Right now, the input tags generated look something like this:
<input id="todo_ids_" name="todo_ids[]" type="checkbox" value="7" />
Every check box has the same id ("todo_ids_"), which is a problem. I suspect the solution is embarrassingly simple, but I'm not seeing it. Any tips?
<%= check_box_tag "todo_ids[]", todo.id, false, :id => "todo_id_#{todo.id}" -%> or whatever you want the id to be.
I consider this a bug with check_box_tag caused by the seemingly hackish nature of manually giving it the name todo_ids[] and the method code calling sanitize_to_id(name). I just ran into this yesterday and I'm contemplating a patch.
I ended up using a solution similar to Ryan's, but as I wrote in the comment I had to make a further change. In the form:
<%= check_box_tag "todo_ids[#{todo.id}]", todo.id %>
In the action called by the form:
Todo.update_all(["completed_at = ?", Time.now], :id => params[:todo_ids].keys)
Note the "params[:todo_ids].keys" at the end, which was a workaround to deal with the odd way the parameters were formatted:
"todo_ids" => {"5"=>"5"}
Can you try this and let us know if it works:
check_box_tag "todo_ids[#{todo.id}]", todo.id %>
This is the expected behaviour of check_box_tag, as this comment on a rejected fix explains.
You can use collection_check_boxes like this (haml syntax, sorry):
# Accumulate todos in a params hash like { todos: { to_complete: [] } }
= collection_check_boxes(:todos, :to_complete, #incomplete_todos, :id, :name) do |todo_builder|
= todo_builder.label do
# This is the result of calling :name on the todo, as specified
# calling the helper
= todo_builder.text
= todo_builder.check_box
Of course you can use partials inside the block, just pass and use the builder inside.
Check more options in the API docs.