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

PeterWong
fonte

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

Yannis
fonte
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>
Skilldrick
fonte
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).

jpemberthy
fonte
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) %>
Peyton
fonte
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.

totalmente assado
fonte
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>
TheEricMiller
fonte
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') %>">
IPIvliev
fonte
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

Fuyi
fonte
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>
Little Phild
fonte
3

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

sorvete
fonte
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>
Joe Chen
fonte
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

Petros Kyriakou
fonte
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'
falta de relatividade
fonte
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

Dan Tello
fonte
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
complacente
fonte
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
Mike Andrianov
fonte
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.

Tom Chen
fonte
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
Benjamin J. Benoudis
fonte
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 .

Juan Diego Gonzales
fonte
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


fonte
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
GN.
fonte
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.

Lwin Htoo Ko
fonte
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" %>
Ken Hibino
fonte
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

Lalit Kumar Maurya
fonte