如何在Rails中为所有活动链接添加“active”类?

19
基本上,我有很多这样的代码:
link_to t('.profile'), business_path(@business), class: '#{'active' if current_page? business_path(@business)}'

这并不是很DRY。

我想知道是否有好的方法可以修改link_to帮助程序本身,以自动将所有链接到当前页面的链接添加一个“active”类。

如果有帮助的话,我可以使用HAML或SLIM。


1
根据Rails 6.1的更新,现在我们有一个HTML类名检查器的帮助程序,链接分别为这里这里 - buncis
13个回答

19

我使用内置的视图辅助函数current_page?编写了一个简单的辅助方法,其中你可以在html_options哈希中指定自定义的class名称。

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

示例(当你在root_path路线上时):

<%= 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>

3
由于 Rails 的 link_to 的工作原理,当您实际传递一个代码块给您的 active_link_to 时,它会出现问题。 - maxhungry
我很遗憾地将其投下反对票,因为多年来它一直是最佳答案,但不幸的是,在传递块时它无法工作。 我建议使用https://dev59.com/62Ml5IYBdhLWcg3wKkLW#71440032作为替代方案。 - David

17
我遇到了同样的需求,这是我的解决方案。
在ApplicationHelper中创建一个方法。
def active_class(link_path)
    current_page?(link_path) ? "active" : ""
end

并且在你的视图中:

    <li class="<%= active_class('/') %>">
      <%= link_to 'HOME', root_path %>
    </li>

3
这是最干净、最简单、最容易的解决方案,无需插入宝石到应用程序辅助程序中,在我看来。 - Devon Kiss

15

这是一个已解决的问题,只需要使用active_link_to gem。您的示例可以简化为以下内容:

= active_link_to t('.profile'), business_path(@business)

1
active_link_to非常棒。它允许您包装链接(例如使用li)并指定其他匹配条件,比如正则表达式。谢谢! - manafire

9

这是编写自己的帮助程序包装link_to的好例子。在你的application_helper.rb中,你可以编写一个名为active_link_to的方法,该方法接受与link_to相同的参数+ current_page,然后像上面所做的那样调用link_to。


在Rails 6.1中,我们有一个助手程序来实现这个功能 https://www.rubydoc.info/docs/rails/ActionView%2FHelpers%2FTagHelper:class_names我的答案基于此 https://dev59.com/62Ml5IYBdhLWcg3wKkLW#67717853抱歉发评论,只是想让大家看到它。 - buncis

6

根据Rails 6.1,我们现在有一个用于HTML类名的帮助程序。

帮助程序示例:

class_names("foo", "bar")
 # => "foo bar"
class_names({ foo: true, bar: false })
 # => "foo"
class_names(nil, false, 123, "", "foo", { bar: true })
 # => "123 foo bar"

你可以像这样使用它。
<%= link_to 'Home', root_path, class: class_names('nav-link', { active: current_page?(root_path) }) %>

它将生成以下类似的HTML:

<a class="nav-link active" href="/">Home</a>

这份文档可以在这里找到。


4

如果您使用Rails 6.1+,这是一个很好的解决方案。

在传递块时也非常有效。

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

2

这是我使用的助手。我添加了可选的“match_text”参数,以增加灵活性(例如,如果我想在实际请求路径是链接目标页的子页面时将链接标记为活动状态)。

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

1

nav.slim:

li class="nav-item"
  = active_link_to 'Users', users_path, "nav-link"

application_helper.rb:

  def active_link_to(title, path, class_css, options = {})
    if current_page?(path)
      class_css = "#{class_css} active"
      options.merge!('aria-current': "page")
    end

    link_to title, path, class: class_css, **options
  end

1

这里有一个基本的例子,你可以在此基础上进行扩展:

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 %>

1

我和 @egyamado 做了同样的事情。我也需要使用 AwesomeIcons,所以:

一个帮助程序:

def active_class?(link_path)
    'active' if current_page?(link_path)
end

这是我的观点:

 <%= 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>&nbsp;My Controller Page
<%end%>

在另一种链接中,例如在 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> 

这对我有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接