I have a simple menu that looks like this:
<ul class="menu">
<li class="active">Home</li>
<li class=""><%= link_to 'Feeds' , feeds_path %></li>
<li class=""><%= link_to 'Accounts' , accounts_path %></li>
</ul>
Class "active" is the style to mark my current page.
I have two questions:
1. How do I "tell" the app what page I am on and ask it to "change" the class to active?
2. Is there a better way to create this menu (maybe driven by the controller or a db table)?
I realize this is a newbie question, but I have been thinking about this for a few days now, have read some tutorials, but none of them really click.
Thanks for your help.
I use the method current_page? to set my active link. It takes a path as its parameter. I create a hash of link texts and paths and iterate over it printing the links. That way I only have to call current_page? one time.
There are gems that can help you, though. Look through these: https://www.ruby-toolbox.com/categories/rails_menu_builders
I have done this recently in ApplicationHelper:
def nav_links
items = [home_link, about_me_link, contact_link]
content_tag :ul, :class => "nav" do
items.collect { |item| concat item}
end
end
def home_link
nav_item_active_if(!#article || #article.type.nil?) do
link_to "Home", root_path
end
end
def about_me_link
nav_item_active_if(#article && #article.type == "About") do
link_to "About Me", article_path(About.first)
end
end
def contact_link
nav_item_active_if(#article && #article.type == "Contact") do
link_to "Contact", article_path(Contact.first)
end
end
def nav_item_active_if(condition, attributes = {}, &block)
if condition
attributes["class"] = "active"
end
content_tag(:li, attributes, &block)
end
In your view you can simply call:
<%= nav_links %>
You can maybe use it as an example.
Not a perfect solution, but you could do the following.
create 3 variables:
#homeActive = ""
#feedsActive = ""
#accountsActive = ""
In the code you provided, set the class to each variable corresponding with name.
<li class=#homeActive>Home</li>
Now in your controller under the home method lets say, set #homeActive = "active", and the other two to "". Repeat for the other methods and this should work.
I used a bunch of 'if's and the current_page? method.
It's ugly but it worked, if someone has a better idea how to do this, I will be happy to learn about it.
if current_page? (root_path)
content_tag(:li , link_to('Home' , root_path), :class => "active")
else
content_tag(:li , link_to('Home' , root_path))
end
Related
Im having a dynamic header depending on the view and action of the controller and it works fine with the actions in "welcomes_controller". But Im not able to trigger a action from a different controller in this case posts_controller.
How can I trigger it correctly? Thanks in advance.
<% if params[:action] == 'index' %>
<%= image_tag("header_background.jpg", alt: "header background", :class => "header_background") %>
<% elsif params[:action] == 'trading' %>
<%= image_tag("trading_background.jpg", alt: "trading background", :class => "header_background") %>
<% elseif ... HOW TO TRIGGER A ACTION FROM A DIFFERENT CONTROLLER? %>
<% end %>
There are two helpers easily availalbe for this: controller_name and action_name. You can use a combo of these for conditional rendering.
That said, personally I feel this is a lot of clutter for the view - I can't help feeling this would be better set elsewhere. In a very simple sense, you could add the following in your controllers:
def index
#header_image = "header_background.jpg"
end
def trading
#header_image = "trading_background.jpg"
end
And then in the view:
image_tag(#header_image, alt: #header_image, :class => "header_background")
Alternatively, you could follow a convention for your images:
image_tag("#{controller_name}/#{action_name}.jpg", alt: action_name, :class => "header_background")
Then ensure your header images are stored in a folder named after the controller, named after the relevant action.
I'd strongly lean towards the latter.
Update: one final option would be to define a helper that handles this, something like, in you view:
= header_image_tag
And somewhere within your /helpers folder something akin to:
def header_image_tag
image_path = case controller_name
when "controller_one"
"header_background.jpg" if action_name == "index"
"trading_background.jpg" if action_name == "trading"
when "controller_two"
# ...
else
"default_image.jpg"
end
image_tag image_path, alt: # ...etc
end
I think the helper has access to controller_name and action_name, though if not you could pass these as args to header_image_tag, or pass the request as an arg and access request.controller_name and request.action_name.
Let me know what you think and how you get on.
For this I use this code. This should work for what you are trying to achieve.
<% if controller.action_name.to_s == "index" %>
Also keep in mind, if you need to be more granular with the specific controller you want to target you can also add the controller name to the check.
<% if controller.controller_name.to_s == "posts" && controller.action_name.to_s == "index" %>
So I'm trying to create a new "transaction" each time I click the button "Purchase", but doesn't seem to work. I get the error "undefined method `[]' for nil:NilClass".
<% #comics.each do |comic|%>
<ul>
<li><%= comic.title %> </li>
<li><%= comic.author %> </li>
<li><%= comic.year %> </li>
<li><%= comic.publisher %> </li>
<% if user_signed_in? %>
<%= button_to 'Purchase', {:controller => "transactions", :action => "create", :seller_id => comic.user_id, :buyer_id=> current_user.id, :comic_id => comic.id} , {:method=>:post} %>
<% end %>
</ul>
This is what you can find in the transactions controller:
def create
#my_transaction = Transaction.new(
buyer_id: params[:transaction][:buyer_id],
seller_id: params[:transaction][:seller_id],
comic_id: params[:transaction][:comic_id]
)
#my_transaction.save
redirect_to "/transactions/"
end
Do you have any idea why this might be happening?
Thanks!
There are a couple of ways you can debug this:
Look in the console logs to see what is being posted in the params hash.
add a puts statement at the top of the create statement to view what is in the params variable e.g.
controller
def create
puts params.inspect
end
I suspect you'll find that the params hash does not have a transaction key and the create method should be
def create
#my_transaction = Transaction.new(
buyer_id: params[:buyer_id],
seller_id: params[:seller_id],
comic_id: params[:comic_id]
)
end
The params[:transaction] is nil, you can see the sent parameters in the log (tail -f log/development.log if the server doesn't log). In your case you access the required data like params[:comic_id]
A few tips:
Never trust the input coming from the client:
:buyer_id=> current_user.id here an attacker could send any ID since the button_to helper will create a html form which is easily accessible using the devtool. Instead check it on the server side. Same goes for the seller_id, you can just fetch the related comic comic = Comic.find params[:comic_id].
You might want to consider an another API approach like POST /comics/1/buy this is a bit more restfull, and you could use the built in path helpers for that url like buy_comic_path(comic)
I have a sidebar that holds a list of posts. I need the corresponding post on my sidebar to have an active class. What i have currently does not work so what is the best way to do this?
def is_active?(path)
current_page?(path) ? "active" : ""
end
<% #posts.each do |post| %>
<%= link_to post.title, post, class: is_active?(posts_path) %>
<% end %>
As I said in my comment, methods ending by ? should return a boolean value. If you decide to go against the convention this will make things harder for us.
I suggest you actually use an active_link_to like it is explained in that question.
However the main problem was that you didn't generate the URL properly for each post :
is_active?(posts_path)
The posts_path is the path to the index and not the individual post resource. You should use something like post_path(post)
You want to do something like this :
First your is_active? method, because it has a ? should return a boolean
def is_active?(path)
current_page?(path)
end
Then you can use it this way (you need to get the URL of the post using the post_path(post) helper)
<% #posts.each do |post| %>
<%= link_to post.title, post, class: ('active' if is_active?(post_path(post))) %>
<% end %>
EDIT : because is_active? does the same thing as current_page? you should simply replace the is_active? code by an alias declaration
alias :is_active? current_page?
I had to develop such a solution some years before. I implemented the following as helper methods to detect an active link. Be aware that this is not the best solution I think. I had to provide a full url. Fell free to edit the code to use a path or a parameter hash instead.
# Returns true or false if the page is displayed that belongs to the given url.
def link_selected?(url)
request_method = request.method.to_sym
begin
url_params = Revolution::Application.routes.recognize_path(
url, {:method=>request_method}
)
rescue Exception => e
{}
end
begin
request_params = Revolution::Application.routes.recognize_path(
request.url, {:method=>request_method}
)
rescue
{}
end
return true if url_params == request_params
return false
end
def is_active?(url)
return link_selected?(url) ? 'active' : nil
end
It's a problem after using the solution here
Best way to add "current" class to nav in Rails 3
def nav_link(link_text, link_path)
class_name = current_page?(link_path) ? 'current' : ''
content_tag(:li, :class => class_name) do
link_to link_text, link_path
end
end
For example, I have localhost/action1, and localhost/action2, each with a nav button.
It works great when user is in either page. In this situation, one of the button would have a 'current' css class.
But, if I set root_path to one of them, let's say is the /action1, so when user visit localhost, the button for action1 won't have a current class
How can I solve add the missing current css class when setting it as the root_path?
You can query the router itself to give you the controller and the action for the current path and just compare it to the current values from params.
def nav_link(text, path)
recognized = Rails.application.routes.recognize_path(path)
klass = "current" if recognized[:controller] == params[:controller] &&
recognized[:action] == params[:action]
content_tag(:li, class: klass) do
link_to(text, path)
end
end
You may wish to highlight your nav menu the following way - I am pretty sure this works irrespective setting the root path:
<ul>
<li class="<%= "current" if params[:controller] == "home" %>">Home</li>
<li class="<%= "current" if params[:controller] == "action1" %>">Action 1</li>
<li class="<%= "current" if params[:controller] == "action2" %>">Action 2</li>
</ul>
You may wish to replace
params[:controller]
with
params[:action]
depending on the level you wish to highlight. I have used this setup in a recent project and it worked for me. Good luck !
Given a Rails 3 App with a menu like:
<ul>
<li>Home</li>
<li>Books</li>
<li>Pages</li>
</ul>
What is a smart way in Rails to have the app know the breadcrumb,,, or when to make one of the LIs show as:
<li class="active">Books</li>
thx
I'm not sure if I will provide you with a smart way but better something than nothing...
If your menu has some links - it is not in your example but I suppose that real menu should have links, not just the items. For example something like this in HAML: (I'm using HAML as writing ERB in text area is pure hell)
%ul
%li= link_to "Home", :controller => "home"
%li= link_to "Books", :controller => "books"
%li= link_to "Pages", :controller => "pages"
Then this helper (pasted from my project) should come handy:
#
# Shows link with style "current" in case when the target controller is same as
# current
# beware: this helper has some limitation - it only accepts hash as URL parameter
#
def menu_link_to(title, url, html_options = {})
unless url.is_a?(Hash) and url[:controller]
raise "URL parameter has to be Hash and :controller has to be specified"
end
if url[:controller] == controller.controller_path
html_options[:class] = "current"
end
link_to(title, url, html_options)
end
With this helper you can replace your "link_to" in the code above with "menu_link_to" and that's it!
An modified version of Radek Paviensky's helper is a tad simpler and more similar to link_to.
# Shows link with style "current" in case when the target controller is same as
# current.
def menu_link_to(title, options = {}, html_options = {})
if current_page?(options)
html_options[:class] ||= []
html_options[:class] << "active" # #TODO catch cases where the class is passed as string instead of array.
end
link_to(title, options, html_options)
end