Najlepszy sposób na dodanie „bieżącej” klasy do nawigacji w Railsach 3


111

Mam kilka stron statycznych w menu nawigacyjnym. Chcę dodać klasę „bieżącą” do aktualnie wyświetlanego elementu.

Sposób, w jaki to robię, polega na dodaniu ton metod pomocniczych (każda dla jednej pozycji), aby sprawdzić kontroler i akcję.

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

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

Czy jest na to lepszy sposób !? Moja obecna droga jest taka głupia ......

Odpowiedzi:


56

Nie jest to prawdziwa odpowiedź, ponieważ używam tego samego sposobu co ty. Właśnie zdefiniowałem metody pomocnicze do testowania dla wielu kontrolerów lub akcji:

W pliku application_helper.rb

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

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

Następnie możesz użyć if controller?("homepage") && action?("index", "show")w swoich widokach lub innych metod pomocniczych…


Twój sposób najlepiej pasuje, gdy niektóre strony są obsługiwane przez różne kontrolery / akcje, ale w tej samej pozycji menu, prawda? Czy napotkałeś więcej zalet ~?
PeterWong

Żadnych korzyści poza zwięzłością składni. Ciekawi mnie, czy ktoś inny ma lepsze rozwiązanie.
Yannis

Zrobiłem tę metodę z wielkim sukcesem. Dodano ten kod do widoku: <% = link_to "Users", users_path, class: (kontroler? ("Users")? 'Selected': nil)%> Naprawdę fajnie, że działa zarówno dla / users, jak i / users / new .
Andreas

1
poprawa mogłaby być controller_namei action_namezamiast params prawdopodobnie
Francesco Belladonna

Dobra robota. Nie myślałem o utrzymaniu tego w prostocie, ale to bardzo ładnie się skaluje. +1
newdark-it

290

Zrobiłem pomocnika o nazwie 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

używany jak:

nav_link 'Home', root_path

który wygeneruje HTML podobny do

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

3
Działa to świetnie z twitterowym ootstrapem, jeśli używasz Twittera bootstrap navbar twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly

41
Zmienić na class_name = current_page? (Link_path)? 'current': nil, jeśli nie chcesz, aby tag klasy był wyświetlany, gdy nie jest na bieżącej stronie
Matthew Hui

1
Jak zmodyfikowałbyś to dla zagnieżdżonych list używanych w listach rozwijanych?
doremi,

9
DRY as a desert
Orlando,

1
Dodałem kilka dodatkowych argumentów extra_classes = nil, id = nil, a następnie wewnątrz content_tag content_tag (: li, class: [class_name, extra_classes], id: id), abyś mógł również dodać własne klasy i identyfikatory do elementów :)
Jon

72

Użyj current_page?pomocnika, aby określić, czy powinieneś przypisać "current"klasę. Na przykład:

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

Zauważ, że możesz również podać ścieżkę (nie tylko skrót opcji), np current_page?(root_path). : .


3
Czy istnieje sposób, aby to zignorować parametry zapytania?
Mohamad,

@Mohamad, możesz to zrobić: current_page? (Controller: 'users', action: 'index')
nilid

26

Używam tej funkcji nav_link (tekst, link) w application_helper.rb (Rails 3), aby wykonać zadanie i przesuwa moje linki paska nawigacyjnego bootstrap na twitterze 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

Przykład:

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

Można to uprościć stosując tę current_page?metodę, podobnie jak w innych odpowiedziach.
Teemu Leisti,

10

Sposób, w jaki to zrobiłem, to dodanie funkcji pomocniczej w application_helper

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

Następnie w nawigacji

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

Spowoduje to sprawdzenie ścieżki odsyłacza pod kątem identyfikatora URI bieżącej strony i zwraca bieżącą klasę lub pusty ciąg.

Nie testowałem tego dokładnie i jestem nowy w RoR (przechodzę po dziesięciu latach z PHP), więc jeśli ma poważną wadę, chciałbym to usłyszeć.

Przynajmniej w ten sposób potrzebujesz tylko 1 funkcji pomocniczej i prostego wywołania w każdym linku.


1
Po prostu problem. Użyłem request.path zamiast request_uri (request_uri nie działał, może problem z wersją railsów?). Twoja odpowiedź jest moim zdaniem czysta i elegancka.
Tony

6

Aby zbudować odpowiedź @Skilldrick ...

Jeśli dodasz ten kod do application.js, upewni się, że wszystkie menu rozwijane z aktywnymi elementami podrzędnymi również zostaną oznaczone jako aktywne ...

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

Aby podsumować kod pomocniczy> Dodaj pomocnika o nazwie 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

używany jak:

nav_link_to 'Home', root_path

który wygeneruje HTML podobny do

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

4

Myślę, że najlepszy sposób to

application_helper.rb:

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

A w menu:

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

czy można zostawić puste "" zajęcia w ten sposób?
Harsha MV

Tak. Może to wyglądać dziwnie, jeśli przeglądasz źródło widząc kilka pustych atrybutów klas, ale jest to poprawny HTML.
kobaltz

Możesz użyć <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, nie wypisze atrybutu klasy dla nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate

4

Wiem, że to przestarzała odpowiedź, ale możesz łatwo zignorować wszystkie te bieżące sprawdzanie strony, używając opakowania pomocniczego link_to, zwanego gemem active_link_to , działa dokładnie tak, jak chcesz, dodaj aktywną klasę do linku do bieżącej strony


4

Oto pełny przykład, jak dodać aktywną klasę na stronie menu bootstrap w widoku railsów.

    <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

Używam niesamowitego klejnotu o nazwie Tabs on Rails .


1
Dzieki za sugestie. Ponieważ moja nawigacja jest tak prosta i jest tylko jedna z małymi przedmiotami, klejnot prawdopodobnie byłby zbyt mocny.
PeterWong,

3

Mam bardziej zwięzłą wersję nav_link, która działa dokładnie tak samo jak link_to, ale jest dostosowana do wyświetlania zawijanego znacznika li.

Umieść poniższy kod w pliku 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

Jeśli spojrzysz na powyższy kod i porównasz go z kodem link_to w url_helper.rb, jedyną różnicą jest to, że sprawdza, czy adres URL jest bieżącą stroną i dodaje klasę „active” do zawijającego znacznika li. Dzieje się tak, ponieważ używam pomocnika nav_link z komponentem nawigacyjnym Twitter Bootstrap który preferuje umieszczanie linków wewnątrz znaczników li i klasę „active” stosowaną do zewnętrznego li.

Zaletą powyższego kodu jest to, że umożliwia on przekazanie bloku do funkcji, tak jak można to zrobić z link_to.

Na przykład lista nawigacji bootstrap z ikonami wyglądałaby następująco:

Szczupły:

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

Wynik:

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

Dodatkowo, tak jak helper link_to, możesz przekazać opcje HTML do nav_link, które zostaną zastosowane do tagu a.

Przykład podania tytułu kotwicy:

Szczupły:

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

Wynik:

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

Niesamowite. Dla mnie była to najbardziej realna opcja właśnie dlatego, że zezwala na bloki. Wielkie dzięki!
Kasperi

3

Dla mnie osobiście użyłem tutaj kombinacji odpowiedzi

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

Używam materialize css, a mój sposób na zwijanie głównych kategorii polega na użyciu poniższego kodu

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

mam nadzieję, że to komuś pomoże


zapomniałem o tym. Dziękuję za wysłanie tego jako przypomnienia.
newdark-it

2

current_page?Metoda nie jest dla mnie za mało elastyczny (powiedzmy ustawić kontroler, ale nie jest to działanie, to będziemy tylko prawdziwy powrót na akcję index kontrolera), więc zrobiłem to na podstawie innych odpowiedzi:

  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

Przykład:

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

Dziękuję za Twoją odpowiedź. Myślę, że twoja jest podobna do zaakceptowanej odpowiedzi :)
PeterWong

Natknąłem się na tę odpowiedź przez Google, ponieważ miałem ten problem, więc pomyślałem, że pomogę wszystkim innym, którzy się z tym
spotkają

2

Tak! Przeczytaj ten artykuł: Lepszy sposób na dodanie „wybranej” klasy do łączy w Railsach

Upuść nav_link_helper.rb do aplikacji / pomocników i może to być tak proste, jak:

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

Pomocnik nav_link działa tak samo jak standardowy helper link_to w Railsach, ale dodaje klasę „wybraną” do łącza (lub jego opakowania), jeśli spełnione są określone kryteria. Domyślnie, jeśli docelowy adres URL linku jest taki sam jak adres URL bieżącej strony, do linku dodawana jest domyślna klasa „wybrane”.

Istota jest tutaj: https://gist.github.com/3279194

AKTUALIZACJA: To jest teraz klejnot: http://rubygems.org/gems/nav_link_to


To jest miłe. Wziąłem do użytku i dodałem modyfikację obsługującą nadawanie nazwy klasy niezaznaczonym elementom, również jako komentarz w istocie.
Teemu Leisti

2

Używam prostego pomocnika, takiego jak ten dla linków najwyższego poziomu, więc /stories/my-storystrona podświetla /storieslink

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

Pokażę moje rozwiązanie:

_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

Zgodnie z odpowiedzią Skilldricka zmienię ją na następującą:

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

aby była bardziej użyteczna.


1

Ta wersja jest oparta na wersji @ Skilldrick, ale umożliwia dodawanie treści html.

W ten sposób możesz:

nav_link "A Page", a_page_path

ale również:

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

lub dowolną inną zawartość html (możesz na przykład dodać ikonę).

Oto pomocnik:

  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

Myślę, że wymyśliłem proste rozwiązanie, które może być pomocne w wielu przypadkach użycia. To pozwala mi:

  • Obsługuje nie tylko zwykły tekst, ale także HTML link_to (np. Dodaj ikonę w linku)
  • Dodaj tylko kilka wierszy kodu do application_helper.rb
  • Dołącz activedo całej nazwy klasy elementu odsyłającego, zamiast być jedyną klasą.

Więc dodaj to do 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

A na swoim szablonie możesz mieć coś takiego:

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

Jako bonus możesz określić lub nie class_namei użyć go w następujący sposób:<div class="<%= current_page?(root_path) %>">

Dzięki wcześniejszym odpowiedziom 1 , 2 i zasobom .


0

wszystkie działają z prostymi paskami nawigacyjnymi, ale co z rozwijanym podmenu? gdy wybrane jest podmenu, pozycja górnego menu powinna być ustawiona jako `` aktualna '' w tym przypadku tabs_on_rails ja być rozwiązaniem


0

Tak rozwiązałem w moim obecnym projekcie.

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

Następnie..

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

0

Mój łatwy sposób -

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

Dzięki.


0

Jeśli chcesz również obsługiwać skrót opcji html w widoku. Na przykład, jeśli chcesz wywołać go z inną klasą lub identyfikatorem CSS, możesz zdefiniować funkcję pomocniczą w ten sposób.

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

W tym widoku wywołaj tego pomocnika w taki sam sposób, w jaki wywołujesz helper link_to

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

0

Utwórz metodę ApplicationHelperjak poniżej.

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

Teraz użyj go w widoku, jak poniżej.

1. Dla wszystkich działań konkretnego kontrolera

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

2. Dla wszystkich działań wielu kontrolerów (oddzielone przecinkami)

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

3. Do konkretnego działania określonego administratora

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

4. Dla określonej akcji wielu kontrolerów (oddzielone przecinkami)

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

5. Dla niektórych konkretnych działań dowolnego konkretnego kontrolera

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

6. Dla niektórych specyficznych akcji wielu kontrolerów (oddzielone przecinkami)

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

Mam nadzieję, że to pomoże

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.