Лучший способ добавить «текущий» класс в навигацию в Rails 3


111

У меня есть несколько статических страниц в меню навигации. Я хочу добавить такой класс, как «текущий», к элементу, который отображается в данный момент.

Я делаю это так, чтобы добавить кучу вспомогательных методов (каждый для одного элемента) для проверки контроллера и действия.

def current_root_class
  'class="current"' if controller_name == "homepage" && action_name == "index" 
end

<ul>
  <li <%= current_root_class %>><%= link_to "Home", root_path %>

Есть ли лучший способ сделать это !? Мой нынешний путь такой глупый ...

Ответы:


56

Не совсем ответ здесь, потому что я использую точно так же, как и вы. Я только что определил вспомогательные методы для тестирования нескольких контроллеров или действий:

В application_helper.rb

  def controller?(*controller)
    controller.include?(params[:controller])
  end

  def action?(*action)
    action.include?(params[:action])
  end

Затем вы можете использовать if controller?("homepage") && action?("index", "show")в своих представлениях или других вспомогательных методах…


Ваш способ лучше всего подходит, когда есть несколько страниц, обрабатываемых разными контроллерами / действиями, но в одном пункте меню, верно? Вы столкнулись с другими преимуществами ~?
PeterWong

Больше никаких преимуществ, кроме краткости синтаксиса. Мне любопытно узнать, есть ли у кого-нибудь лучшее решение.
Яннис

Я сделал этот метод с большим успехом. Добавлен этот код в представление: <% = link_to "Users", users_path, class: (controller? ("Users")? 'Selected': nil)%> Действительно замечательно, что он работает как для / users, так и для / users / new .
Андреас

1
возможно улучшение controller_nameи action_nameвместо params
Francesco Belladonna

Отлично сработано. Я не думал о том, чтобы сделать это простым, но это очень хорошо масштабируется. +1
newdark-it

290

Я сделал помощника по имени nav_link:

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

используется как:

nav_link 'Home', root_path

который будет создавать HTML как

<li class="current"><a href="/">Home</a></li>

3
Это отлично работает с twitter ootstrap, если вы используете twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly,

41
Изменить на class_name = current_page? (Link_path)? 'current': nil, если вы не хотите, чтобы тег класса отображался, когда он не на текущей странице
Мэтью Хуэй,

1
Как бы вы изменили это для вложенных списков, используемых в раскрывающихся списках?
doremi

9
СУХОЙ как пустыня
Орландо

1
Я добавил несколько дополнительных аргументов extra_classes = nil, id = nil, затем внутри content_tag content_tag (: li, class: [class_name, extra_classes], id: id), чтобы вы также могли добавлять свои собственные классы и идентификаторы к элементам :)
Джон

72

Используйте current_page?помощник, чтобы определить, следует ли вам назначать "current"класс. Например:

<%= 'active' if current_page?(home_about_path) %>

Обратите внимание , вы также можете пройти путь (не только хэш опций), например: current_page?(root_path).


3
Есть ли способ заставить это игнорировать параметры запроса?
Mohamad

@Mohamad, вы можете сделать это: current_page? (Контроллер: 'users', действие: 'index')
nilid

26

Я использую эту функцию nav_link (текст, ссылка) в application_helper.rb (Rails 3), чтобы выполнить свою работу, и она скатывает за меня ссылки на панель навигации bootstrap twitter 2.0.

def nav_link(text, link)
    recognized = Rails.application.routes.recognize_path(link)
    if recognized[:controller] == params[:controller] && recognized[:action] == params[:action]
        content_tag(:li, :class => "active") do
            link_to( text, link)
        end
    else
        content_tag(:li) do
            link_to( text, link)
        end
    end
end

Пример:

<%=nav_link("About Us", about_path) %>

Это можно упростить, используя current_page?метод, как и в других ответах.
Теему Лейсти

10

Я сделал это так, чтобы добавить вспомогательную функцию в application_helper

def current_class?(test_path)
  return 'current' if request.request_uri == test_path
  ''
end

Затем в навигаторе

<%= link_to 'Home', root_path, :class => current_class?(root_path) %>

Это проверяет путь ссылки на текущий uri страницы и возвращает либо ваш текущий класс, либо пустую строку.

Я не тестировал это тщательно, и я очень новичок в RoR (перешел через десятилетие с PHP), поэтому, если в этом есть серьезный недостаток, я хотел бы его услышать.

По крайней мере, таким образом вам понадобится только 1 вспомогательная функция и простой вызов в каждой ссылке.


1
Просто проблема. Я использовал request.path вместо request_uri (request_uri не работал, возможно, проблема с версией rails?). На мой взгляд, ваш ответ ясен и элегантен.
Тони

6

Чтобы построить ответ @Skilldrick ...

Если вы добавите этот код в application.js, он обеспечит, чтобы любые раскрывающиеся меню с активными дочерними элементами также были отмечены как активные ...

$('.active').closest('li.dropdown').addClass('active');

Чтобы повторить вспомогательный код> Добавьте помощника под названием nav_link:

def nav_link_to(link_text, link_path)
  class_name = current_page?(link_path) ? 'active' : ''

  content_tag(:li, :class => class_name) do
    link_to link_text, link_path
  end
end

используется как:

nav_link_to 'Home', root_path

который будет создавать HTML как

<li class="active"><a href="/">Home</a></li>

4

Я думаю, что лучший способ - это

application_helper.rb:

def is_active(controller, action)       
  params[:action] == action && params[:controller] == controller ? "active" : nil        
end

И в меню:

<li class="<%= is_active('controller', 'action') %>">

можно ли оставлять такой пустой класс ""?
Harsha MV

Да. Это может показаться странным, если вы просматриваете источник, видя кучу пустых атрибутов класса, но это правильный HTML.
kobaltz

Вы можете использовать <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, он не будет печатать атрибут класса для nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate

4

Я знаю, что это устаревший ответ, но вы можете легко игнорировать все эти проверки текущей страницы, используя вспомогательную оболочку link_to , называемую active_link_to gem, она работает именно так, как вы хотите, добавьте активный класс в ссылку на текущую страницу


4

Вот полный пример того, как добавить активный класс на страницу меню начальной загрузки в представлении rails.

    <li class="<%= 'active' if current_page?(root_path) %>"><%= link_to 'Home', controller: "welcome" %></li>
    <li class="<%= 'active' if current_page?(about_path) %>"><%= link_to 'About us', about_path %></li>
   <li class="<%= 'active' if current_page?(contact_path) %>"><%= link_to 'Contact us', contact_path %></li>

3

Я использую потрясающий гем под названием Tabs on Rails .


1
Спасибо за предложение. Поскольку моя навигация настолько проста, и есть только одна с мелкими элементами, драгоценный камень, вероятно, будет чрезмерно мощным.
PeterWong

3

У меня есть более сжатая версия nav_link, которая работает точно так же, как link_to, но настроена для вывода оборачивающего тега li.

Поместите следующее в свой application_helper.rb

def nav_link(*args, &block)
    if block_given?
      options      = args.first || {}
      html_options = args.second
      nav_link(capture(&block), options, html_options)
    else
      name         = args[0]
      options      = args[1] || {}
      html_options = args[2]

      html_options = convert_options_to_data_attributes(options, html_options)
      url = url_for(options)

      class_name = current_page?(url) ? 'active' : nil

      href = html_options['href']
      tag_options = tag_options(html_options)

      href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
      "<li class=\"#{class_name}\"><a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a></li>".html_safe
    end
  end

Если вы посмотрите на приведенный выше код и сравните его с кодом link_to в url_helper.rb, единственное отличие состоит в том, что он проверяет, является ли URL-адрес текущей страницей, и добавляет класс «active» в оборачивающий тег li. Это потому, что я использую помощник nav_link с компонентом nav Twitter Bootstrap, который предпочитает, чтобы ссылки были заключены в теги li, а «активный» класс применялся к внешнему li.

Хорошая вещь в приведенном выше коде заключается в том, что он позволяет вам передавать блок в функцию, как вы можете сделать с link_to.

Например, список навигации начальной загрузки со значками будет выглядеть так:

Тонкий:

ul.nav.nav-list
  =nav_link root_path do
    i.icon-home
    |  Home
  =nav_link "#" do
    i.icon-user
    |  Users

Вывод:

<ul class="nav nav-list">
  <li class="active">
    <a href="/">
      <i class="icon-home"/>
      Home
    </a>
  </li>
  <li>
    <a href="#">
      <i class="icon-users"/>
      Users
    </a>
  </li>
</ul>

Кроме того, как и в случае с помощником link_to, вы можете передать параметры HTML в nav_link, который будет применен к тегу a.

Пример передачи заголовка для якоря:

Тонкий:

ul.nav.nav-list
  =nav_link root_path, title:"Home" do
    i.icon-home
    |  Home
  =nav_link "#", title:"Users" do
    i.icon-user
    |  Users

Вывод:

<ul class="nav nav-list">
  <li class="active">
    <a href="/" title="Home">
      <i class="icon-home"/>
      Home
    </a>
  </li>
  <li>
    <a href="#" title="Users">
      <i class="icon-users"/>
      Users
    </a>
  </li>
</ul>

Потрясающие. Для меня это был наиболее жизнеспособный вариант именно потому, что он позволяет блокировать. Благодаря тонну!
Kasperi

3

Лично я использовал здесь комбинацию ответов

<li class="<%= 'active' if current_page?(inventory_index_path) %>"><a href="#">Menu</a></li>

Я использую материализацию css, и мой способ сделать основные категории сворачиваемыми - это использовать код ниже

$('.active').closest(".collapsible.collapsible-accordion")
            .find(".collapsible-header")
            .click();

надеюсь, это поможет кому-то


забыл об этом. Спасибо, что разместили это как напоминание.
newdark-it

2

Этот current_page?метод для меня недостаточно гибкий (скажем, вы устанавливаете контроллер, но не действие, тогда он вернет true только для действия индекса контроллера), поэтому я сделал это на основе других ответов:

  def nav_link_to(link_text, link_path, checks=nil)
    active = false
    if not checks.nil?
      active = true
      checks.each do |check,v|
        if not v.include? params[check]
          active = false
          break
        end
      end
    end

    return content_tag :li, :class => (active ? 'active' : '') do
      link_to link_text, link_path
    end
  end

Пример:

nav_link_to "Pages", pages_url, :controller => 'pages'

Спасибо за ваш ответ. Я думаю, что ваш на самом деле похож на принятый ответ :)
PeterWong

Я наткнулся на этот ответ через Google, так как у меня была эта проблема, поэтому я подумал, что помогу всем остальным, кто столкнется с этим :)
безответственность

2

Ага! Ознакомьтесь с этой статьей: Лучший способ добавить «выбранный» класс к ссылкам в Rails

Перетащите nav_link_helper.rb в app / helpers, и это может быть очень просто:

<%= nav_link 'My_Page', 'http://example.com/page' %>

Помощник nav_link работает так же, как стандартный помощник Rails link_to, но добавляет «выбранный» класс к вашей ссылке (или ее оболочке), если соблюдены определенные критерии. По умолчанию, если целевой URL-адрес ссылки совпадает с URL-адресом текущей страницы, к ссылке добавляется класс по умолчанию «selected».

Здесь есть суть: https://gist.github.com/3279194

ОБНОВЛЕНИЕ: теперь это жемчужина: http://rubygems.org/gems/nav_link_to


Это хорошо. Я начал использовать и добавил модификацию для поддержки присвоения имени класса невыбранным элементам, а также в качестве комментария в сути.
Теему Лейсти

2

Я использую такой простой помощник для ссылок верхнего уровня, чтобы /stories/my-storyстраница выделяла /storiesссылку

def nav_link text, url

  active = (url == request.fullpath || (url != '/' && request.fullpath[0..(url.size-1)] == url))

  "<li#{ active ? " class='selected'" : '' }><a href='#{url}'>#{text}</a></li>".html_safe

end

2

Позвольте мне показать свое решение:

_header.html.erb :

  <ul class="nav">
    <%= nav_tabs(@tabs) %> 
  </ul>

application_helper.rb :

 def nav_tabs(tabs=[])
    html = []
    tabs.each do |tab| 
      html << (content_tag :li, :class => ("current-page" if request.fullpath.split(/[\??]/)[0] == tab[:path]) do
        link_to tab[:path] do
          content_tag(:i, '', :class => tab[:icon]) +
          tag(:br) +
          "#{tab[:name]}"
        end
      end)        
    end

    html.join.html_safe
  end

application_controller.rb :

before_filter :set_navigation_tabs

private
def set_navigation_tabs
  @tabs = 
    if current_user && manager?
      [
        { :name => "Home", :icon => "icon-home", :path => home_index_path },
        { :name => "Portfolio", :icon => "icon-camera", :path => portfolio_home_index_path },
        { :name => "Contact", :icon => "icon-envelope-alt", :path => contact_home_index_path }
      ]
    elsif current_user && client?
      ...
    end

1

Согласно ответу Skilldrick , я изменю его на следующее:

def nav_link(*args, &block)
  is_active = current_page?(args[0]) || current_page?(args[1])
  class_name = is_active ? 'active' : nil

  content_tag(:li, class: class_name) do
    link_to *args, &block
  end
end

чтобы сделать его более полезным.


1

Эта версия основана на версии @ Skilldrick, но позволяет добавлять html-контент.

Таким образом, вы можете:

nav_link "A Page", a_page_path

но также:

nav_link a_page_path do
  <strong>A Page</strong>
end

или любой другой HTML-контент (например, вы можете добавить значок).

Вот помощник:

  def nav_link(name = nil, options = nil, html_options = nil, &block)
    html_options, options, name = options, name, block if block_given?
    options ||= {}

    html_options = convert_options_to_data_attributes(options, html_options)

    url = url_for(options)
    html_options['href'] ||= url

    class_name = current_page?(url) ? 'current' : ''
    content_tag(:li, :class => class_name) do  
      content_tag(:a, name || url, html_options, &block)
    end
  end

1

Думаю, я придумал простое решение, которое может оказаться полезным во многих случаях использования. Это позволяет мне:

  • Поддержка не только обычного текста, но и HTML внутри link_to(например, добавление значка внутри ссылки)
  • Добавьте всего несколько строк кода в application_helper.rb
  • Добавить activeк полному имени класса элемента ссылки вместо того, чтобы быть единственным классом.

Итак, добавьте это в application_helper.rb:

def active_class?(class_name = nil, path)
  class_name ||= ""
  class_name += " active" if current_page?(path)
  class_name.strip!
  return class_name
end

А в вашем шаблоне может быть что-то вроде этого:

<div class="col-xs-3">
  <%= link_to root_path, :class => active_class?("btn btn-outline-primary", root_path) do %>
    <i class="fa fa-list-alt fa-fw"></i>
  <% end %>
</div>

В качестве бонуса вы можете указать или нет class_nameи использовать его так:<div class="<%= current_page?(root_path) %>">

Благодаря предыдущим ответам 1 , 2 и ресурсам .


0

все это работает с простыми панелями навигации, но как насчет раскрывающегося подменю? когда выбрано подменю, верхний пункт меню должен быть сделан «текущим», в этом случае tabs_on_rails я буду решением


0

Вот как я решил в своем текущем проекте.

def class_if_current_page(current_page = {}, *my_class)
    if current_page?(current_page)
      my_class.each do |klass|
        "#{klass} "
      end
    end
  end

Затем..

li = link_to company_path 
    class: %w{ class_if_current_page( { status: "pending" }, "active" ), "company" } do  
      Current Company

0

Мой легкий путь -

application.html.erb,

<div class="navbar">
    <div class="<%= @menu1_current %> first-item"><a href="/menu1"> MENU1 </a></div>
    <div class="<%= @menu2_current %>"><a href="/menu2"> MENU2 </a></div>
    <div class="<%= @menu3_current %>"><a href="/menu3"> MENU3 </a></div>
    <div class="<%= @menu4_current %> last-item"><a href="/menu4"> MENU4 </a></div>
</div>

main_controller.erb,

class MainController < ApplicationController
    def menu1
        @menu1_current = "current"
    end

    def menu2
        @menu2_current = "current"
    end

    def menu3
        @menu3_current = "current"
    end

    def menu4
        @menu4_current = "current"
    end
end

Спасибо.


0

Если вы также хотите поддерживать хэш опций html в представлении. Например, если вы хотите вызвать его с другим классом CSS или идентификатором, вы можете определить вспомогательную функцию следующим образом.

def nav_link_to(text, url, options = {})
  options[:class] ||= ""
  options[:class] += " active"
  options[:class].strip!
  link_to text, url, options
end

Итак, в представлении вызовите этот помощник так же, как вы вызываете помощник link_to

<%= nav_link_to "About", about_path, class: "my-css-class" %>

0

Создайте метод, ApplicationHelperкак показано ниже.

def active controllers, action_names = nil
  class_name = controllers.split(",").any? { |c| controller.controller_name == c.strip } ? "active" : ""
  if class_name.present? && action_names.present?
    return action_names.split(",").any? { |an| controller.action_name == an.strip } ? "active" : ""
  end
  class_name
end

Теперь используйте его, как показано ниже.

1. Для всех действий любого конкретного контроллера

<li class="<%= active('controller_name')%>">
....
</li>

2. Для всех действий многих контроллеров (через запятую)

<li class="<%= active('controller_name1,controller_name2')%>">
....
</li>

3. Для конкретных действий любого конкретного контроллера

<li class="<%= active('controller_name', 'action_name')%>">
....
</li>

4. Для определенных действий многих контроллеров (через запятую)

<li class="<%= active('controller_name1,controller_name2', 'action_name')%>">
....
</li>

5. Для каких-то специфических действий любого конкретного контроллера

<li class="<%= active('controller_name', 'index, show')%>">
....
</li>

6. Для некоторых специфических действий многих контроллеров (через запятую)

<li class="<%= active('controller_name1,controller_name2', 'index, show')%>">
....
</li>

Надеюсь, поможет

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.