Il modo migliore per aggiungere la classe "corrente" a nav in Rails 3


111

Ho alcune pagine statiche in un menu di navigazione. Voglio aggiungere una classe come "corrente" all'elemento attualmente visualizzato.

Il modo in cui lo faccio è aggiungere tonnellate di metodi di supporto (ciascuno per un elemento) per controllare il controller e l'azione.

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

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

C'è un modo migliore per farlo !? Il mio modo attuale è così stupido ...

Risposte:


56

Non è proprio una risposta qui, perché sto usando più o meno come te. Ho appena definito metodi di supporto per testare più controller o azioni:

In application_helper.rb

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

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

Quindi puoi utilizzare if controller?("homepage") && action?("index", "show")nelle tue visualizzazioni o altri metodi di supporto ...


Il tuo modo si adatta meglio quando ci sono alcune pagine gestite da controller / azioni diversi ma nella stessa voce di menu, giusto? Hai riscontrato più vantaggi ~?
PeterWong

Nessun vantaggio in più se non la concisione della sintassi. Sono curioso di vedere se qualcun altro ha una soluzione migliore.
Yannis

Ho fatto questo metodo con grande successo. Aggiunto questo codice alla vista: <% = link_to "Users", users_path, class: (controller? ("Users")? 'Selected': nil)%> Davvero carino che funziona sia per / users che / users / new .
Andreas

1
un miglioramento potrebbe essere controller_namee action_nameinvece dei parametri probabilmente
Francesco Belladonna

Molto bene. Non pensavo di mantenerlo semplice, ma scala molto bene. +1
newdark-it

290

Ho creato un aiutante chiamato 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

usato come:

nav_link 'Home', root_path

che produrrà HTML come

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

3
Funziona benissimo con Twitter ootstrap se stai utilizzando la barra di navigazione bootstrap di Twitter twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly,

41
Passare a class_name = current_page? (Link_path)? 'current': nil se non vuoi che il tag class venga mostrato quando non è nella pagina corrente
Matthew Hui

1
Come lo modificheresti per gli elenchi nidificati utilizzati nei menu a discesa?
doremi

9
ASCIUTTO come un deserto
Orlando

1
Ho aggiunto alcuni argomenti extra extra_classes = nil, id = nil quindi all'interno di content_tag content_tag (: li, class: [class_name, extra_classes], id: id) in modo che tu possa aggiungere anche le tue classi e i tuoi ID agli elementi :)
Jon

72

Usa l' current_page?helper per determinare se assegnare o meno la "current"classe. Per esempio:

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

Nota è anche possibile passare un percorso (non solo un hash di opzioni), ad esempio: current_page?(root_path).


3
C'è un modo per far sì che questo ignori i parametri della query?
Mohamad

@ Mohamad, puoi farlo: current_page? (Controller: 'users', action: 'index')
nilid

26

Uso questa funzione nav_link (text, link) in application_helper.rb (Rails 3) per portare a termine il lavoro e mi fa rotolare i link della barra di navigazione bootstrap twitter 2.0 per me.

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

Esempio:

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

Ciò potrebbe essere semplificato utilizzando il current_page?metodo, come in altre risposte.
Teemu Leisti

10

Il modo in cui l'ho fatto è aggiungere una funzione di supporto in application_helper

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

Quindi nel navigatore,

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

Questo verifica il percorso del collegamento rispetto all'URI della pagina corrente e restituisce la classe corrente o una stringa vuota.

Non l'ho testato a fondo e sono molto nuovo in RoR (trasferito dopo un decennio con PHP) quindi se questo ha un grave difetto mi piacerebbe ascoltarlo.

Almeno in questo modo hai solo bisogno di 1 funzione di supporto e una semplice chiamata in ogni collegamento.


1
Solo un problema. Ho usato request.path invece di request_uri (request_uri non funzionava, forse problema con la versione di rails?). La tua risposta è pulita ed elegante secondo me.
Tony

6

Per costruire la risposta di @Skilldrick ...

Se aggiungi questo codice a application.js, assicurerai che anche tutti i menu a discesa con figli attivi siano contrassegnati come attivi ...

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

Per ricapitolare il codice di supporto> Aggiungi un helper chiamato 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

usato come:

nav_link_to 'Home', root_path

che produrrà HTML come

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

4

Penso che il modo migliore sia

application_helper.rb:

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

E nel menu:

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

va bene lasciare una classe vuota "" come quella?
Harsha MV,

Sì. Può sembrare strano se visualizzi il codice sorgente vedendo un mucchio di attributi di classe vuoti, ma è HTML valido.
kobaltz

Puoi usare <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, non stamperà un attributo di classe per nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate

4

So che è una risposta obsoleta , ma puoi facilmente ignorare tutti questi controlli della pagina corrente usando un link_to helper wrapper, chiamato active_link_to gem, funziona esattamente come vuoi, aggiungi una classe attiva al link della pagina corrente


4

Ecco l'esempio completo su come aggiungere una classe attiva nella pagina del menu bootstrap nella vista 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

Uso una fantastica gemma chiamata Tabs on Rails .


1
Grazie per il suggerimento. Dal momento che la mia navigazione è così semplice e ce n'è solo una con piccoli oggetti, la gemma sarebbe probabilmente sovraccarica.
PeterWong

3

Ho una versione più succinta di nav_link che funziona esattamente come link_to, ma è personalizzata per produrre un tag li avvolgente.

Metti quanto segue nel tuo 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 guardi il codice sopra e lo confronti con il codice link_to in url_helper.rb, l'unica differenza è che controlla se l'URL è la pagina corrente e aggiunge la classe "attiva" a un tag li avvolgente. Questo perché sto usando l'helper nav_link con il componente nav di Twitter Bootstrap che preferisce che i collegamenti siano inseriti all'interno dei tag li e la classe "attiva" applicata al li esterno.

La cosa bella del codice sopra è che ti permette di passare un blocco nella funzione, proprio come puoi fare con link_to.

Ad esempio, un elenco di navigazione bootstrap con icone sarebbe simile a:

Sottile:

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

Produzione:

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

Inoltre, proprio come l'helper link_to, puoi passare le opzioni HTML a nav_link, che verrà applicato al tag a.

Un esempio di passaggio di un titolo per l'ancora:

Sottile:

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

Produzione:

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

Eccezionale. Per me questa è stata l'opzione più praticabile proprio perché consente i blocchi. Grazie mille!
Kasperi

3

Per me personalmente ho usato una combinazione di risposte qui

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

Sto usando materialize css e il mio modo di rendere le categorie principali pieghevoli è usare il codice sottostante

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

spero che aiuti qualcuno


dimenticato di questo. Grazie per aver postato questo messaggio come promemoria.
newdark-it

2

Il current_page?metodo non è abbastanza flessibile per me (supponiamo che imposti un controller ma non un'azione, quindi restituirà true solo sull'azione index del controller), quindi l'ho fatto in base alle altre risposte:

  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

Esempio:

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

La ringrazio per la risposta. Penso che la tua sia simile alla risposta accettata in realtà :)
PeterWong

Ho trovato questa risposta tramite Google poiché avevo questo problema, quindi ho pensato di aiutare anche tutti gli altri che si imbattono in questo :)
inrelatività

2

Sì! Dai un'occhiata a questo articolo: Un modo migliore per aggiungere una classe "selezionata" ai collegamenti in Rails

Trascina nav_link_helper.rb in app / helper e può essere facile come:

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

L'helper nav_link funziona proprio come l'helper link_to standard di Rails, ma aggiunge una classe 'selected' al tuo link (o al suo wrapper) se vengono soddisfatti determinati criteri. Per impostazione predefinita, se l'URL di destinazione del collegamento è lo stesso URL dell'URL della pagina corrente, al collegamento viene aggiunta una classe predefinita di "selezionato".

C'è un succo qui: https://gist.github.com/3279194

AGGIORNAMENTO: Questo ora è un gioiello: http://rubygems.org/gems/nav_link_to


Questo è carino. L'ho utilizzato e ho aggiunto una modifica per supportare l'assegnazione di un nome di classe agli elementi non selezionati, anche come commento nel succo.
Teemu Leisti

2

Uso un semplice aiuto come questo per i collegamenti di primo livello, quindi la /stories/my-storypagina evidenzia il /storiescollegamento

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

Fammi mostrare la mia soluzione:

_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

Secondo la risposta di Skilldrick , lo cambierò come segue:

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

per renderlo molto più utile.


1

Questa versione è basata su quella di @ Skilldrick ma ti permette di aggiungere contenuti html.

Quindi, puoi fare:

nav_link "A Page", a_page_path

ma anche:

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

o qualsiasi altro contenuto html (puoi aggiungere un'icona per esempio).

Qui l'aiutante è:

  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

Penso di aver trovato una soluzione semplice che potrebbe essere utile per molti casi d'uso. Questo mi permette di:

  • Supporta non solo testo normale ma HTML all'interno link_to (ad es. Aggiungi un'icona all'interno del collegamento)
  • Aggiungi solo poche righe di codice a application_helper.rb
  • Aggiungi activeal nome della classe intera dell'elemento link invece di essere l'unica classe.

Quindi, aggiungi questo 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 sul tuo modello puoi avere qualcosa del genere:

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

Come bonus puoi specificare o meno a class_namee usarlo in questo modo:<div class="<%= current_page?(root_path) %>">

Grazie alle risposte precedenti 1 , 2 e alle risorse .


0

tutti questi funzionano con semplici barre di navigazione, ma per quanto riguarda il sottomenu a discesa? quando viene selezionato un sottomenu la voce di menu in alto dovrebbe essere resa 'corrente' in questo caso tabs_on_rails me essere la soluzione


0

È così che ho risolto nel mio progetto attuale.

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

Poi..

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

0

Il mio modo semplice -

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

Grazie.


0

Se vuoi anche supportare le opzioni html hash nella vista. Ad esempio, se vuoi chiamarlo con un'altra classe CSS o id, puoi definire la funzione di supporto in questo modo.

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

Quindi nella vista, chiama questo helper nello stesso modo in cui chiameresti link_to helper

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

0

Crea un metodo ApplicationHelpercome di seguito.

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

Ora usalo in vista come di seguito casi d'uso.

1. Per tutte le azioni di qualsiasi controller specifico

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

2. Per tutte le azioni di molti controller (con virgola separata)

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

3. Per azioni specifiche di qualsiasi controller specifico

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

4. Per azioni specifiche di molti controller (con virgola separata)

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

5. Per alcune azioni specifiche di qualsiasi controller specifico

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

6. Per alcune azioni specifiche di molti controller (con virgola separata)

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

Spero che sia d'aiuto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.