This is a good case for writing your own helper that wraps the link_to. In your application_helper.rb you can write a method active_link_to
that takes the same params as link_to + current_page, and then just calls link_to like you are doing above.
Add an 'active' class to all active links in rails?
-
27-06-2022 - |
Frage
Basically, I have a lot of code that looks like this:
link_to t('.profile'), business_path(@business), class: '#{'active' if current_page? business_path(@business)}'
which isn't very DRY.
I was wondering if anyone knows a good way to modify the link_to helper itself to automatically add an 'active' class to all links to the current page.
If it helps, I'm open to using HAML or SLIM.
Lösung 4
Andere Tipps
I wrote simple helper method using build in view helper current_page?
when you can specify custom class
name in html_options
hash.
def active_link_to(name = nil, options = nil, html_options = nil, &block)
active_class = html_options[:active] || "active"
html_options.delete(:active)
html_options[:class] = "#{html_options[:class]} #{active_class}" if current_page?(options)
link_to(name, options, html_options, &block)
end
Examples (when you are on root_path
route):
<%= active_link_to "Main", root_path %>
# <a href="/" class="active">Main</a>
<%= active_link_to "Main", root_path, class: "bordered" %>
# <a href="/" class="bordered active">Main</a>
<%= active_link_to "Main", root_path, class: "bordered", active: "disabled" %>
# <a href="/" class="bordered disabled">Main</a>
It's a solved problem, just use active_link_to gem. Your example simplifies to this:
= active_link_to t('.profile'), business_path(@business)
I faced same requirement and here is my solution.
Create a method within ApplicationHelper
def active_class(link_path)
current_page?(link_path) ? "active" : ""
end
And inside your view:
<li class="<%= active_class('/') %>">
<%= link_to 'HOME', root_path %>
</li>
Here's the helper I use. I add an optional "match_text" parameter for added flexibility (for instance, if I want to mark a link as active when the actual request path is a child page of the link's destination.)
def link_to_active(text, destination, options = {})
match_text = options.delete(:match_text)
classes = options[:class].present? ? options[:class].split(" ") : []
classes << "active" if request.fullpath.downcase == destination.downcase || (match_text && request.fullpath.downcase.include?(match_text.downcase))
options = options.except(:class)
options.merge!(:class => classes.join(" ")) unless classes.empty?
link_to(text, destination, options)
end
as per rails 6.1 now we have helper for html class name
the helper example
class_names("foo", "bar")
# => "foo bar"
class_names({ foo: true, bar: false })
# => "foo"
class_names(nil, false, 123, "", "foo", { bar: true })
# => "123 foo bar"
you could use it like this
<%= link_to 'Home', root_path, class: class_names('nav-link', { active: current_page?(root_path) }) %>
it will produce html like this
<a class="nav-link active" href="/">Home</a>
the doc is here
I did the same that @egyamado. I needed to use AwesomeIcons too, so:
A helper:
def active_class?(link_path)
'active' if current_page?(link_path)
end
And it was my view:
<%= link_to my_controller_page_path,
:title => "My Controller Page",
:class => "other_name_class #{active_class?(my_controller_page_path)}" do %>
<i class="fa fa-fighter-jet"></i> My Controller Page
<%end%>
In another kind of Link, for example inside a Li.
#In this case I put a extra validation in root_path
<li class="nav-class <%=active_class?(my_controller_page_path)%> <%='active' if current_page?(root_path) %>">
<%= link_to my_controller_page_path,
:title => "Page 1",
:class => "other_name_class" do %>
Page 1
<%end%>
</li>
<li class="nav-class <%=active_class?(my_controller_page_2_path)%>">
<%= link_to my_controller_page_2_path,
:title => "Page 2",
:class => "other_name_class" do %>
Page 2
<%end%>
</li>
It worked for me.
This is my custom method to handle this issue.
def active_link_to(name = nil, options = nil, html_options = nil, &block)
if current_page?(options)
active_class = html_options[:active_class] ? html_options[:active_class] : 'has-text-danger'
html_options[:class] << "#{html_options[:class]} #{active_class}"
end
link_to(name, options, html_options, &block)
end
html_options[:active_class]
is a custom hash.
Now I can dynamically change styles of my active link.
<%= active_link_to "Menu", root_path, class: 'has-text-dark', active_class: 'has-text-danger' %>
Here's a basic example from which you can expand:
module ApplicationHelper
def active_link_to(text, path, **opts, &block)
css_class = opts[:class]
case opts[:endpoint]
when String then css_class.concat(' active') if current_page? opts[:endpoint]
when Array then css_class.concat(' active') if opts[:endpoint].include? request.path
end
if block_given?
link_to path, class: css_class, &block
else
link_to text, path, class: css_class
end
end
end
# app/views/layout/_navbar.html.erb
# Using multiple endpoints
<%= active_link_to 'Home', root_path, class: 'some-class', endpoint: ['', '/', '/home'] %>
# Using a single endpoint
<%= active_link_to 'Admin', admin_path, class: 'some-class', endpoint: '/admin' %>
# Using a block
<%= active_link_to admin_path, class: 'some-class', endpoint: '/admin' do %>
<h1>Administration</h1>
<% end %>
Great solution if you're using rails 6.1+.
Also works great when passing a block.
def active_link_to(text = nil, path = nil, **opts, &block)
link = block_given? ? text : path
opts[:class] = class_names(opts[:class], { active: current_page?(link) })
if block_given?
link_to link, opts, &block
else
link_to text, path, opts
end
end
Use link_to_unless_current
and then give it the look of an active link in CSS.