I'm still a beginner with ruby and rails, and now I'm googling about methods for creating a tabbed menu, marking the list element of the currently active controller with a css class "current". There are many hits on google, but I haven't found any that I manage to get working.
I have my menu here:
<ul>
<li class="current"><%= link_to 'Home', root_path %> </li>
<li><%= link_to 'Browse songs', page_path('browse') %> </li>
<li><%= link_to 'Add song', new_song_path %> </li>
<li><%= link_to 'Request song', artists_path %> </li>
<li><%= link_to 'My ReChord', artists_path %> </li>
<li><%= link_to 'Help', page_path('help') %> </li>
<li id="search"><form><input type="search" placeholder="Type here to find a song or an artist"/></form> </li>
<li class="notab">
<% if user_signed_in? %>
<%= link_to 'Sign out', destroy_user_session_path %>
<% else %>
<%= link_to 'Sign in', new_user_session_path %> or
<%= link_to 'sign up', new_user_registration_path %>
<% end %>
</li>
</ul>
Now I have class="current" hard coded on the Home tab. However, when clicking for example Browse songs, I want the class="current" to be moved to the corresponding list element for that line.
Note that I have some links that just is the route path (like new_song_path) and some links that are sub pages, like page_path('help'). I need it to work for both these types of links.
Can you provide me with either a good tutorial suitable for my two days long experience with rails, or (preferably) example code that might fit perfectly on my list above? Thanks in advance!
Create a helper to build that menu for you:
def menu_builder(page_id)
tabs = ['home','store','faq']
content = ""
tabs.each do |tab|
content << if page_id == tab
content_tag('li', content_tag('a', tab, :href => nil ), :class => 'current') + " "
else
content_tag('li', content_tag('a', tab, :href => "/#{tab}" )) + " "
end
end
content
end
Or my own short version of it:
def menu_builder(page_id)
["home", "store", "faq"].map { |tab|
%{<li class="#{page_id == tab ? "active" : "inactive"}">#{tab.capitalize}</li>}
}.join("\n")
end
page_id is an identifier of some page and should be defined in your controllers.
class Foo < ApplicationController
def faq
#page_id = 'faq'
...
end
end
Then just use it in the template:
<ul>
<%= menu_builder(#page_id) %>
<li class="search">...</li>
</ul>
If you want something more flexible, checkout the tabs_on_rails plugin I created to solve exactly this common pattern.
In your template use the tabs_tag helper to create your tab.
<% tabs_tag do |tab| %>
<%= tab.home 'Homepage', root_path %>
<%= tab.dashboard 'Dashboard', dashboard_path %>
<%= tab.account 'Account', account_path %>
<% end %>
The example above produces the following HTML output.
<ul>
<li>Homepage</li>
<li>Dashboard</li>
<li>Account</li>
</ul>
The usage is similar to the Rails route file. You create named tabs with the syntax tab.name_of_tab.
The name you use creating a tab is the same you’re going to refer to in your controller when you want to mark a tab as the current tab.
class DashboardController < ApplicationController
set_tab :dashboard
end
Now, if the action belongs to DashboardController, the template will automatically render the following HTML code.
<ul>
<li>Homepage</li>
<li class="custom"><span>Dashboard</span></li>
<li>Account</li>
</ul>
Take a look an this plugin:
http://github.com/paolodona/rails-widgets
It has lots of cool components including navigation.
Documentation: https://github.com/paolodona/rails-widgets/wiki
I created tabulous to solve this problem. There is a separate tab configuration file where you can describe how you want your tabs to behave. For example:
Tabulous.setup do
tabs do
pictures_tab do
text { 'Pictures' }
link_path { pictures_path }
visible_when { true }
enabled_when { true }
active_when { in_action('any').of_controller('pictures') }
end
music_tab do
text { 'Music' }
link_path { songs_path }
visible_when { true }
enabled_when { current_user.has_music? }
active_when { in_action('any').of_controller('songs') }
end
profile_tab do
text { 'My Profile' }
link_path { account_path(current_user) }
visible_when { true }
enabled_when { true }
active_when { in_actions('show', 'edit', 'update').of_controller('accounts') }
end
end
end
Read the tabulous documentation to learn more.
Related
What to change give an active class to a li depending of the route.
I have this routes:
/profile/2
/profile/2/info
/profile/2/contact
I have a menu
<ul>
<li>
<%= link_to 'Profile', profile_path(current_user), class: current_class_contains?('/profiles') %>
</li>
<li>
<%= link_to 'Info', info_profile_path(current_user), class: current_class_contains?('/info') %>
</li>
<li>
<%= link_to 'Contact', contact_profile_path(current_user), class: current_class_contains?('/contact') %>
</li>
</ul>
In application_helper
module ApplicationHelper
def current_class_contains?(test_path)
return "active" if request.path.match(test_path)
""
end
def current_class?(test_path)
return "active" if request.path == test_path
""
end
end
The problem that I got is that if I'm in /profile/2/info or /profile/2/contact the Profile li is also given the active class.
I can't figure it out. Any ideas?
Use current_page?, Something like following
return "active" if current_page? test_path
& calling
current_class_contains?(profile_path(current_user))
I would use current_page? to build a helper method like this:
def indicating_link_to(name, url)
css_class = 'active' if current_page?(url)
link_to(name, url, class: css_class)
end
And use that helper method like this in your view:
<ul>
<li>
<%= indicating_link_to 'Profile', profile_path(current_user) %>
</li>
<li>
<%= indicating_link_to 'Info', info_profile_path(current_user) %>
</li>
<li>
<%= indicating_link_to 'Contact', contact_profile_path(current_user) %>
</li>
</ul>
And with the new class_names method that will be introduced with Ruby on Rails 6.1 it will be even simpler:
def indicating_link_to(name, url)
link_to(name, url, class: class_names(active: current_page?(url)))
end
I am trying to set an li class of active based on what page the user is on.
I have 4 navigation elements and they all look something like this:
<% if #activeLi == "home" %>
<li class="active">
<% else %>
<li>
<% end %>
<%= link_to :controller => "home" do %>
<span>Home</span>
<% end %>
</li>
and then in each controller I just set #activeLi like this:
def index
#activeLi = "about"
end
I know this is pretty basic stuff, but i'm just wondering if there is an easier way to do this?
Well I know one way you can simplify this and thats by getting rid of the need to use those nasty instance variables.
<li class="<%= controller_name == "home" ? 'active': '' %>">
<%= link_to :controller => "home" do %>
<span>Home</span>
<% end %>
</li>
So in my application.html.erb I have my navigational structure that looks something like this:
<div id="navigation">
<ul class="pills">
<% if current_page?(:controller => 'welcome', :action => 'index') %>
<li><%= link_to "Profile", vanity_path(:vname => current_user.username) %></li>
<li><%= link_to "Settings", settings_path %></li>
<li><%= link_to "Sign Out", signout_path %></li>
<% elsif !current_page?(:controller => 'welcome', :action => 'index') %>
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Profile", vanity_path(:vname => current_user.username) %></li>
<li><%= link_to "Settings", settings_path %></li>
<li><%= link_to "Sign Out", signout_path %></li>
<% end %>
</ul>
</div>
However, what I would like to do, is once they are on any of the pages in the navigation, it applies a class active to the respective link.
So for instance, if the user is on mydomain.com/johnbrown which is the Profile link, the rails helper would look something like this:
link_to "Profile", vanity_path(:vname => current_user.username), :class => "active".
But how do I do that in a programmatic way, so I am not duplicating content? i.e. how do I get that functionality for all the pages in my navigation and write it as DRY as possible?
Thanks.
This is a really great question. I've never really been happy with the solutions I've seen or come up with. Maybe we can get one together here.
Here is what I've tried in the past
I've made a helper that returns a hash with :class defined since I use HAML
def active_tab(path)
request.path.match(/^#{path}/) ? { :class => 'active' } : {}
end
ex usage:
= link_to "Dashboard", dashboard_path, active_tab("#{dashboard_path}$")
Or an alternative along the same lines
def active_class(path)
request.path =~ /#{path}/ ? 'active' : nil
end
ex usage:
= link_to 'Presentations', admin_presentations_path, :class => "#{active_class('presentations')}"
I would love to see some other suggestions on this.
I found this answer for a bootstrap related navbar but you could easily use it with your nav.
Answer taken from here
You can use helper for handle "current_page?", example a method :
module ApplicationHelper
def is_active?(link_path)
if current_page?(link_path)
"active"
else
""
end
end
end
example bootstrap navbar
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="#">Title</a>
<ul class="nav">
<li class="active">Home</li>
<li>Link</li>
<li>Link</li>
</ul>
</div>
</div>
So, on view looks like
<li class="<%= is_active?(some_path) %>">
<%= link_to "name path", some_path %>
</li>
For Haml
Just simple looks like :
%ul.nav
%li{class: current_page?(some_path) && 'active'}
= link_to "About Us", some_path
You may define a helper method in application_helper.rb
def create_link(text, path)
class_name = current_page?(path) ? 'my_class' : ''
content_tag(:li, class: class_name) do
link_to text, path
end
end
Now you can use like:
create_link 'xyz', any_path which would render as
<li class="my_class">
xyz
</li>
Hope it helps!
Why don't you just take the redundant parts out of the if else block? Also take a look at 'link_to_unless'
<div id="navigation">
<ul class="pills">
<li><%= link_to_unless(current_page?(:controller => 'welcome', :action => 'index'), "Home", root_path %></li>
<li><%= link_to "Profile", vanity_path(:vname => current_user.username) %></li>
<li><%= link_to "Settings", settings_path %></li>
<li><%= link_to "Sign Out", signout_path %></li>
</ul>
</div>
then I would add some jQuery to inject active class into the link that matches the window.location pattern.
I have a ul filled with links in my layout/application.html.erb and want the current location link be marked with class="active".
Now I'm using:
<%= link_to 'About Us', { :controller => 'aboutus' }, :class => "menu#{' active' if params[:controller] == 'aboutus'}" %>
But it looks pretty nasty to me.
Anyone has a better idea?
The link_to_unless_current method doesn't actually create a link, nor does it add a class of active. If you'd still like to do that, you can use the current_page method to check if the current page matches the route you specified:
<ul id="main_nav">
<li><%= link_to "Search", search_path, :class => ('active' if current_page?(search_path)) %></li>
</ul>
Or if you'd like to add the class on the wrapping element:
<li class="<%= 'active' if current_page?(search_path) %>">
<%= link_to "Search", search_path %>
</li>
You can use the helper method similar to link_to
its called "link_to_unless_current".
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html ( find the details here )
HTH
An example for a root path with a li tag, i tried to do it with a content_tag, but no luck for the if case in a elegant way
<li<%= " class='active'".html_safe if current_page?(root_path) %>>
<%= link_to "Home", root_path %>
</li>
i have some issues when i keep the class in blank with bootstrap(if i remember well)
I have a list of tabs at the top of my application that I include in a general layout in application.html.erb. They look like this:
<li class="current"><%= link_to "Home", provider_path(current_user.id), :method=> "GET"%> </li>
<li><%= link_to "Edit Profile", edit_student_path(current_user.id) %> </li>
<li><%= link_to "Search", provider_search_path %> </li>
I want to change the selected tab to the "current" one, when I hit that page. So when I click Edit Profile and the Edit Profile page loads, the tabs should appear as follows:
<li><%= link_to "Home", provider_path(current_user.id), :method=> "GET"%> </li>
<li class="current"><%= link_to "Edit Profile", edit_student_path(current_user.id) %> </li>
<li><%= link_to "Search", provider_search_path %> </li>
Is there a way to do this outside of adding javascript to the page which is displayed? Or if there is what is generally best practice for doing this in the DRYest way possible.
Thanks
You can use controller.class == and controller.action_name == to figure out exactly which controller and action you are on
so it would be something like
<li class="<%= controller.class == ProviderController and controller.action_name == 'show' ? 'current' : '' %>"><%= link_to "Home", provider_path(current_user.id), :method=> "GET"%> </li>
<li class="<%= controller.class == StudentController and controller.action_name == 'edit' ? 'current' : '' %>"><%= link_to "Edit Profile", edit_student_path(current_user.id) %> </li>
<li class="<%= controller.class == ProviderController and controller.action_name == 'search' ? 'current' : '' %>"><%= link_to "Search", provider_search_path %> </li>
I believe there are some ways to get the current url for the page you are on, but then your "active" styling will be dependent on only getting to that action via that path, which may not always be the case depending on the routes, this way will ensure the view shows what is true based on what was actually run, not what the url is in the address bar
You could try something like:
<li class="<%= controller.controller_path == 'provider' ? 'current' : '' %>"><%= link_to "Home", provider_path(current_user.id), :method=> "GET"%> </li>
<li class="<%= controller.controller_path == 'student' ? 'current' : '' %>"><%= link_to "Edit Profile", edit_student_path(current_user.id) %> </li>
<li class="<%= controller.controller_path == 'search' ? 'current' : '' %>"><%= link_to "Search", provider_search_path %> </li>
...and just check which controller you're coming from.
Take a look at TabsOnRails
You could just do this:
<%= current_page?(:controller => 'your_controller', :action => 'index') ? 'current' : '' %>
I made a helper for this that can accept any number of arguments which is useful for nested resources, but it also accepts a single controller name just like the other answers.
application-helper.rb:
def is_active(*links)
links.each { |link| return "active" if params[:controller] == link }
end
application.html.erb:
<li class="<%=is_active('home')%>">...</li>
Or
Example usage with HAML & nested resource(will be active for any of the provided controller names):
applcation.html.haml:
%li{:class => is_active('blogs', 'comments')}
When you switch pages you could pass something like #current_tab back to the erb from the controller methods. Then use #current_tab to decide which li should be the current class. Alternatively, you could give each li and id or some unique attribute and simply change the class with your JavaScript framework of choice.
I'm so not claiming this is the best way to do this, however I am brave enough to post what I came up with :)
Some example menu links from my layout:
<li class="nav-calendar"><%= menu_link_to 'teachers', 'show_date', 'Calendar', calendar_url %></li>
<li class="nav-announcements"><%= menu_link_to 'announcements', nil, 'Announcements', announcements_path %></li>
Then I created this helper:
def menu_link_to(*args, &block)
controller = args.shift
action = args.shift
if controller == controller_name && (action.nil? || action == action_name)
if args.third.nil?
args.push({:class => 'selected'})
else
args.third.merge!({:class => 'selected'})
end
end
link_to *args, &block
end
I did this in the application helper and it seems to work fine:
def current_page(path)
"active" if current_page?(path)
end
Then call like this:
<li class="<%= current_page(root_path)%>"> <%= link_to "Home", root_path %></li>