Melhor maneira de adicionar a classe “atual” para navegar no Rails 3


111

Eu tenho algumas páginas estáticas em um menu de navegação. Quero adicionar uma classe como "atual" ao item que está sendo exibido no momento.

A forma como estou fazendo isso é adicionar toneladas de métodos auxiliares (cada um para um item) para verificar o controlador e a ação.

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

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

Existe alguma maneira melhor de fazer isso !? Meu jeito atual é tão estúpido ...

Respostas:


56

Não é realmente uma resposta aqui, porque estou usando exatamente da mesma maneira que você. Acabei de definir métodos auxiliares para testar vários controladores ou ações:

Em application_helper.rb

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

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

Então você pode usar if controller?("homepage") && action?("index", "show")em suas visualizações ou outros métodos auxiliares ...


Sua maneira se adequa melhor quando há algumas páginas tratadas por diferentes controladores / ações, mas no mesmo item de menu, certo? Você encontrou mais vantagens ~?
PeterWong,

Sem mais vantagens, exceto a concisão da sintaxe. Estou curioso para ver se alguém tem uma solução melhor.
Yannis de

Eu fiz esse método com grande sucesso. Adicionado este código à visualização: <% = link_to "Users", users_path, class: (controller? ("Users")? 'Selected': nil)%> Muito legal que funciona para / users e / users / new .
Andreas

1
uma melhoria poderia ser controller_namee em action_namevez de parâmetros provavelmente
Francesco Belladonna

Bem feito. Eu não pensei em manter isso simples, mas a escala é muito boa. +1
newdark-it

290

Eu fiz um ajudante chamado 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 irá produzir HTML como

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

3
Isso funciona muito bem com o twitter ootstrap se você estiver usando a barra de navegação do twitter bootstrap twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly

41
Mudar para class_name = current_page? (Link_path)? 'current': nulo se você não quiser que a tag de classe seja exibida quando não estiver na página atual
Matthew Hui

1
Como você modificaria isso para listas aninhadas usadas em menus suspensos?
doremi

9
SECO como um deserto
Orlando

1
Eu adicionei alguns argumentos extras extra_classes = nil, id = nil e dentro de content_tag content_tag (: li, class: [class_name, extra_classes], id: id) para que você possa adicionar suas próprias classes e ids aos elementos também :)
Jon

72

Use o current_page?auxiliar para determinar se você deve ou não atribuir a "current"classe. Por exemplo:

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

Note que você também pode passar um caminho (não somente um hash de opções), por exemplo: current_page?(root_path).


3
Existe uma maneira de ignorar os parâmetros de consulta?
Mohamad

@Mohamad, você pode fazer isso: current_page? (Controlador: 'usuários', ação: 'índice')
nilid

26

Eu uso esta função nav_link (texto, link) em application_helper.rb (Rails 3) para fazer o trabalho e ela rola meus links de bootstrap da barra de navegação do Twitter 2.0 para mim.

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

Exemplo:

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

Isso poderia ser simplificado usando o current_page?método, como em outras respostas.
Teemu Leisti

10

A forma como fiz isso é adicionar uma função auxiliar no application_helper

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

Então, na navegação,

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

Isso testa o caminho do link contra o uri da página atual e retorna sua classe atual ou uma string vazia.

Eu não testei isso completamente e sou muito novo no RoR (mudando depois de uma década com o PHP), então, se houver uma grande falha, adoraria ouvi-la.

Pelo menos assim você só precisa de 1 função auxiliar e uma chamada simples em cada link.


1
É só um problema. Usei request.path em vez de request_uri (request_uri não estava funcionando, talvez problema de versão do rails?). Sua resposta é limpa e elegante em minha opinião.
Tony

6

Para construir a resposta de @Skilldrick ...

Se você adicionar este código ao application.js, ele garantirá que todos os menus suspensos com filhos ativos também sejam marcados como ativos ...

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

Para recapitular o código de suporte> Adicione um auxiliar chamado 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 irá produzir HTML como

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

4

Eu acho que a melhor maneira é

application_helper.rb:

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

E no menu:

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

há problema em deixar uma classe em branco "" assim?
Harsha MV

Sim. Pode parecer estranho se você visualizar o código-fonte vendo um monte de atributos de classe vazios, mas é um HTML válido.
kobaltz de

Você pode usar <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, ele não imprimirá um atributo de classe para nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate

4

Eu sei que é uma resposta desatualizada, mas você pode facilmente ignorar todas as verificações da página atual usando um link_to helper wrapper, chamado active_link_to gem, funciona exatamente como você deseja, adicione uma classe ativa ao link da página atual


4

Aqui está o exemplo completo, sobre como adicionar uma classe ativa na página do menu de bootstrap na visualização 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

Eu uso uma joia incrível chamada Tabs on Rails .


1
Obrigado pela sugestão. Como minha navegação é tão simples e há apenas uma com poucos itens, a gema provavelmente seria sobrecarregada.
PeterWong

3

Eu tenho uma versão mais sucinta de nav_link que funciona exatamente como link_to, mas é personalizado para gerar uma tag li envolvente.

Coloque o seguinte em seu 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

Se você olhar o código acima e compará-lo com o código link_to em url_helper.rb, a única diferença é que ele verifica se o url é a página atual e adiciona a classe "ativa" a uma tag li envolvente. Isso ocorre porque estou usando o auxiliar nav_link com o componente nav do Twitter Bootstrap, que prefere que os links sejam agrupados dentro de tags li e a classe "ativa" aplicada ao li externo.

O bom do código acima é que ele permite que você passe um bloco para a função, assim como você pode fazer com link_to.

Por exemplo, uma lista de navegação bootstrap com ícones se pareceria com:

Fino:

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

Resultado:

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

Além disso, assim como o helper link_to, você pode passar opções de HTML para nav_link, que será aplicado a uma tag.

Um exemplo de passagem de um título para a âncora:

Fino:

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

Resultado:

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

Impressionante. Para mim esta foi a opção mais viável justamente porque permite bloqueios. Muito obrigado!
Kasperi

3

Para mim, pessoalmente, usei uma combinação de respostas aqui

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

Estou usando materialize css e minha maneira de tornar as categorias principais recolhíveis é usando o código abaixo

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

espero que ajude alguém


esqueci disso. Obrigado por postar isso como um lembrete.
newdark-it de

2

O current_page?método não é flexível o suficiente para mim (digamos que você defina um controlador, mas não uma ação, então ele só retornará verdadeiro na ação de índice do controlador), então fiz isso com base nas outras respostas:

  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

Exemplo:

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

Obrigado pela sua resposta. Acho que a sua resposta é semelhante à aceita na verdade :)
PeterWong

Eu encontrei essa resposta por meio do Google, pois estava tendo esse problema, então pensei em ajudar todos os outros que se
deparassem

2

Sim! Confira este artigo: Uma maneira melhor de adicionar uma classe 'selecionada' aos links no Rails

Solte nav_link_helper.rb em app / helpers e pode ser tão fácil quanto:

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

O auxiliar nav_link funciona como o helper link_to padrão do Rails, mas adiciona uma classe 'selecionada' ao seu link (ou seu wrapper) se certos critérios forem atendidos. Por padrão, se o url de destino do link for o mesmo url da página atual, uma classe padrão 'selecionado' é adicionada ao link.

Há uma essência aqui: https://gist.github.com/3279194

ATUALIZAÇÃO: agora é uma joia: http://rubygems.org/gems/nav_link_to


Isso é legal. Usei e adicionei uma modificação para dar suporte a dar um nome de classe aos elementos não selecionados, também, como um comentário na essência.
Teemu Leisti

2

Eu uso um auxiliar simples como este para links de nível superior para que a /stories/my-storypágina destaque o /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

Deixe-me mostrar minha solução:

_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

De acordo com a resposta de Skilldrick , vou mudar para o seguinte:

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 torná-lo muito mais útil.


1

Esta versão é baseada na de @ Skilldrick, mas permite adicionar conteúdo html.

Assim, você pode fazer:

nav_link "A Page", a_page_path

mas também:

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

ou qualquer outro conteúdo html (você pode adicionar um ícone, por exemplo).

Aqui, o ajudante é:

  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

Acho que encontrei uma solução simples que pode ser útil para muitos casos de uso. Isso me permite:

  • Suporta não apenas texto simples, mas HTML interno link_to(por exemplo, adicionar um ícone dentro do link)
  • Adicione apenas algumas linhas de código para application_helper.rb
  • Anexe activeao nome da classe inteira do elemento de link em vez de ser a única classe.

Então, adicione isso 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

E em seu modelo, você pode ter algo assim:

<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 bônus você pode especificar ou não um class_namee usá-lo assim:<div class="<%= current_page?(root_path) %>">

Graças às respostas anteriores 1 , 2 e recursos .


0

tudo isso funciona com barras de navegação simples, mas e o submenu suspenso? quando um sub-menu é selecionado o item do menu superior deve ser tornado 'atual' neste caso tabs_on_rails me ser a solução


0

Foi assim que resolvi no meu projeto atual.

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

Então..

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

0

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

Obrigado.


0

Se você também deseja oferecer suporte a opções de hash html na visualização. Por exemplo, se você deseja chamá-lo com outra classe CSS ou id, você pode definir a função auxiliar desta forma.

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

Então, na view, chame este helper da mesma forma que você chamaria link_to helper

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

0

Crie um método ApplicationHelperconforme abaixo.

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

Agora use-o para ver os casos de uso abaixo.

1. Para todas as ações de qualquer controlador específico

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

2. Para todas as ações de muitos controladores (com vírgula separada)

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

3. Para ação específica de qualquer controlador específico

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

4. Para ação específica de muitos controladores (com vírgula separada)

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

5. Para algumas ações específicas de qualquer controlador específico

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

6. Para algumas ações específicas de muitos controladores (com vírgulas separadas)

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

Espero que ajude

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.