La mejor manera de agregar la clase "actual" a la navegación en Rails 3

111

Tengo algunas páginas estáticas en un menú de navegación. Quiero agregar una clase como "actual" al elemento que se muestra actualmente.

La forma en que lo estoy haciendo es agregar toneladas de métodos auxiliares (cada uno para un elemento) para verificar el controlador y la acción.

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

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

¿¡Hay alguna manera mejor de hacerlo !? Mi forma actual es tan estúpida ...

PeterWong
fuente

Respuestas:

56

No es realmente una respuesta aquí, porque lo estoy usando de la misma manera que tú. Acabo de definir métodos de ayuda para probar múltiples controladores o acciones:

En application_helper.rb

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

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

Luego, puede usarlo if controller?("homepage") && action?("index", "show")en sus vistas u otros métodos de ayuda ...

Yannis
fuente
Su forma se adapta mejor cuando hay algunas páginas manejadas por diferentes controladores / acciones pero en el mismo elemento de menú, ¿verdad? ¿Ha encontrado más ventajas ~?
PeterWong
No más ventajas excepto la concisión de la sintaxis. Tengo curiosidad por ver si alguien más tiene una mejor solución.
Yannis
Hice este método con gran éxito. Se agregó este código a la vista: <% = link_to "Users", users_path, class: (controller? ("Users")? 'Selected': nil)%> Realmente bueno que funciona tanto para / users como para / users / new .
Andreas
1
una mejora podría ser controller_namey en action_namelugar de params probablemente
Francesco Belladonna
Bien hecho. No pensé en mantener esto simple, pero se escala muy bien. +1
newdark-it
290

Hice un ayudante llamado 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

usado como:

nav_link 'Home', root_path

que producirá HTML como

<li class="current"><a href="/">Home</a></li>
Skilldrick
fuente
3
Esto funciona muy bien con twitter ootstrap si está utilizando la barra de navegación de arranque de twitter twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly
41
¿Cambiar a class_name = current_page? (Link_path)? 'actual': nulo si no desea que la etiqueta de la clase se muestre cuando no está en la página actual
Matthew Hui
1
¿Cómo modificaría esto para las listas anidadas que se usan en los menús desplegables?
Doremi
9
DRY as a desert
Orlando
1
He agregado algunos argumentos adicionales extra_classes = nil, id = nil luego dentro de content_tag content_tag (: li, class: [class_name, extra_classes], id: id) para que pueda agregar sus propias clases e identificaciones a los elementos también :)
Jon
72

Utilice el current_page?asistente para determinar si debe o no asignar la "current"clase. Por ejemplo:

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

Nota También puede pasar una ruta de acceso (no sólo un hash de opciones), por ejemplo: current_page?(root_path).

jpemberthy
fuente
3
¿Hay alguna forma de hacer que esto ignore los parámetros de consulta?
Mohamad
@Mohamad, puedes hacer esto: current_page? (Controlador: 'usuarios', acción: 'índice')
nilid
26

Utilizo esta función nav_link (texto, enlace) en application_helper.rb (Rails 3) para hacer el trabajo y me envía los enlaces de la barra de navegación de twitter 2.0 de bootstrap.

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

Ejemplo:

<%=nav_link("About Us", about_path) %>
Peyton
fuente
Esto podría simplificarse utilizando el current_page?método, como en otras respuestas.
Teemu Leisti
10

La forma en que lo hice es agregar una función de ayuda en el application_helper

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

Luego en la navegación

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

Esto prueba la ruta del enlace con el uri de la página actual y devuelve su clase actual o una cadena vacía.

No lo he probado a fondo y soy muy nuevo en RoR (me mudé después de una década con PHP), así que si esto tiene un defecto importante, me encantaría escucharlo.

Al menos de esta manera solo necesita 1 función auxiliar y una simple llamada en cada enlace.

completamente horneado
fuente
1
Solo un problema. Usé request.path en lugar de request_uri (request_uri no funcionaba, ¿tal vez un problema con la versión de rieles?). Tu respuesta es limpia y elegante en mi opinión.
Tony
6

Para construir a partir de la respuesta de @Skilldrick ...

Si agrega este código a application.js, se asegurará de que cualquier menú desplegable con niños activos también se marque como activo ...

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

Para recapitular el código de apoyo> Agregue un ayudante llamado 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

usado como:

nav_link_to 'Home', root_path

que producirá HTML como

<li class="active"><a href="/">Home</a></li>
TheEricMiller
fuente
4

Creo que la mejor manera es

application_helper.rb:

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

Y en el menú:

<li class="<%= is_active('controller', 'action') %>">
IPIvliev
fuente
¿Está bien dejar una clase en blanco "" así?
Harsha MV
Si. Puede parecer extraño si ve el código fuente y ve un montón de atributos de clase vacíos, pero es HTML válido.
kobaltz
Puede usar <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, no imprimirá un atributo de clase para nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate
4

Sé que es una respuesta desactualizada, pero puede ignorar fácilmente todas estas verificaciones de la página actual mediante el uso de un contenedor link_to helper, llamado active_link_to gem, funciona exactamente lo que desea, agregue una clase activa al enlace de la página actual

fuyi
fuente
4

Aquí está el ejemplo completo, sobre cómo agregar una clase activa en la página del menú de arranque en la vista de rieles.

    <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>
Pequeño phild
fuente
3

Utilizo una joya increíble llamada Tabs on Rails .

helado
fuente
1
Gracias por la sugerencia. Dado que mi navegador es tan simple y solo hay uno con elementos pequeños, la gema probablemente se sobrecargaría.
PeterWong
3

Tengo una versión más sucinta de nav_link que funciona exactamente como link_to, pero está personalizada para generar una etiqueta li envolvente.

Ponga lo siguiente en su 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

Si observa el código anterior y lo compara con el código link_to en url_helper.rb, la única diferencia es que verifica si la URL es la página actual y agrega la clase "activa" a una etiqueta li envolvente. Esto se debe a que estoy usando el ayudante nav_link con el componente de navegación de Twitter Bootstrap, que prefiere que los enlaces se envuelvan dentro de las etiquetas li y que la clase "activa" se aplique al li exterior.

Lo bueno del código anterior es que le permite pasar un bloque a la función, al igual que puede hacer con link_to.

Por ejemplo, una lista de navegación de arranque con iconos se vería así:

Delgado:

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

Salida:

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

Además, al igual que link_to helper, puede pasar opciones HTML a nav_link, que se aplicará a la etiqueta a.

Un ejemplo de pasar un título para el ancla:

Delgado:

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

Salida:

<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>
Joe Chen
fuente
Increíble. Para mí, esta fue la opción más viable precisamente porque permite bloques. ¡Gracias una tonelada!
Kasperi
3

Para mí, personalmente, utilicé una combinación de respuestas aquí.

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

Estoy usando materialize css y mi forma de hacer que las categorías principales sean plegables es usando el siguiente código

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

espero que ayude a alguien

Petros Kyriakou
fuente
me olvidé de esto. Gracias por publicar esto como recordatorio.
newdark-it
2

El current_page?método no es lo suficientemente flexible para mí (digamos que configura un controlador pero no una acción, entonces solo devolverá verdadero en la acción de índice del controlador), así que hice esto en función de las otras respuestas:

  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

Ejemplo:

nav_link_to "Pages", pages_url, :controller => 'pages'
irrelatividad
fuente
Gracias por su respuesta. Creo que la suya es similar a la respuesta aceptada en realidad :)
PeterWong
Encontré esta respuesta a través de Google ya que estaba teniendo este problema, así que pensé en ayudar a todos los demás que se encuentren con esto también :)
unrelativity
2

¡Sí! Consulte este artículo: Una mejor manera de agregar una clase 'seleccionada' a enlaces en rieles

Suelta nav_link_helper.rb en app / helpers y puede ser tan fácil como:

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

El ayudante nav_link funciona igual que el ayudante link_to estándar de Rails, pero agrega una clase 'seleccionada' a su enlace (o su envoltorio) si se cumplen ciertos criterios. De forma predeterminada, si la URL de destino del enlace es la misma que la URL de la página actual, se agrega una clase predeterminada de 'seleccionado' al enlace.

Aquí hay una esencia: https://gist.github.com/3279194

ACTUALIZACIÓN: Esto ahora es una joya: http://rubygems.org/gems/nav_link_to

Dan Tello
fuente
Esto es bonito. Lo utilicé y agregué una modificación para admitir dar un nombre de clase a los elementos no seleccionados, también, como un comentario en la esencia.
Teemu Leisti
2

Utilizo un ayudante simple como este para los enlaces de nivel superior para que la /stories/my-storypágina resalte el /storiesenlace

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
complista
fuente
2

Déjame mostrarte mi solución:

_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
Mike Andrianov
fuente
1

De acuerdo con la respuesta de Skilldrick , la cambiaré por la siguiente:

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

para hacerlo mucho más útil.

Tom Chen
fuente
1

Esta versión se basa en la de @ Skilldrick, pero le permite agregar contenido html.

Por lo tanto, puede hacer:

nav_link "A Page", a_page_path

pero también:

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

o cualquier otro contenido html (puede agregar un icono, por ejemplo).

Aquí el ayudante es:

  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
Benjamin J. Benoudis
fuente
1

Creo que se me ocurrió una solución simple que podría ser útil para muchos casos de uso. Esto me permite:

  • Admite no solo texto sin formato sino también HTML en el interior link_to(por ejemplo, agregue un icono dentro del enlace)
  • Agregue solo algunas líneas de código a application_helper.rb
  • Agregue activeal nombre de clase completo del elemento de enlace en lugar de ser la única clase.

Entonces, agregue esto a 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

Y en su plantilla puede tener algo como esto:

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

Como beneficio adicional , puede especificar o no un class_namey usarlo así:<div class="<%= current_page?(root_path) %>">

Gracias a las respuestas anteriores 1 , 2 y recursos .

Juan Diego Gonzales
fuente
0

todos estos funcionan con barras de navegación simples, pero ¿qué pasa con el submenú desplegable? cuando se selecciona un submenú, el elemento del menú superior debe hacerse 'actual' en este caso, tabs_on_rails, yo seré la solución


fuente
0

Así es como resolví en mi proyecto actual.

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

Luego..

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

Mi manera fácil -

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

Gracias.

Lwin Htoo Ko
fuente
0

Si también desea admitir el hash de opciones html en la vista. Por ejemplo, si desea llamarlo con otra clase o id de CSS, puede definir la función auxiliar de esta manera.

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

Entonces, en la vista, llame a este asistente de la misma manera que llamaría link_to helper

<%= nav_link_to "About", about_path, class: "my-css-class" %>
Ken Hibino
fuente
0

Cree un método ApplicationHelpercomo se muestra a continuación.

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

Ahora utilícelo a la vista como los siguientes casos de uso.

1. Para todas las acciones de cualquier controlador específico

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

2. Para todas las acciones de muchos controladores (separados por comas)

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

3. Para la acción específica de cualquier controlador específico

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

4. Para acciones específicas de muchos controladores (separados por comas)

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

5. Para algunas acciones específicas de cualquier controlador específico

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

6. Para algunas acciones específicas de muchos controladores (con comas separadas)

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

Espero eso ayude

Lalit Kumar Maurya
fuente