Meilleur moyen d'ajouter la classe «actuelle» à la navigation dans Rails 3

111

J'ai des pages statiques dans un menu de navigation. Je veux ajouter une classe comme "courant" à l'élément qui est actuellement affiché.

La façon dont je le fais est d'ajouter des tonnes de méthodes d'assistance (chacune pour un élément) pour vérifier le contrôleur et l'action.

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

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

Y a-t-il une meilleure façon de le faire !? Ma façon actuelle est tellement stupide ......

PeterWong
la source

Réponses:

56

Pas vraiment une réponse ici, car j'utilise tout à fait de la même manière que vous. Je viens de définir des méthodes d'assistance pour tester plusieurs contrôleurs ou actions:

Dans application_helper.rb

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

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

Ensuite, vous pouvez utiliser if controller?("homepage") && action?("index", "show")dans vos vues ou d'autres méthodes d'aide…

Yannis
la source
Votre chemin convient le mieux quand il y a des pages gérées par différents contrôleurs / actions mais dans le même élément de menu, non? Avez-vous rencontré plus d'avantages ~?
PeterWong
Plus d'avantage que la concision de la syntaxe. Je suis curieux de voir si quelqu'un d'autre a une meilleure solution.
Yannis
J'ai fait cette méthode avec beaucoup de succès. Ajout de ce code à la vue: <% = link_to "Users", users_path, class: (controller? ("Users")? 'Selected': nil)%> Vraiment bien que cela fonctionne à la fois pour / users et / users / new .
Andreas
1
une amélioration pourrait être controller_nameet action_nameau lieu de paramètres probablement
Francesco Belladonna
Bien joué. Je n'ai pas pensé à garder cela simple mais cela évolue très bien. +1
newdark-it
290

J'ai fait un assistant appelé 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

utilisé comme:

nav_link 'Home', root_path

qui produira du HTML comme

<li class="current"><a href="/">Home</a></li>
Skilldrick
la source
3
Cela fonctionne très bien avec twitter ootstrap si vous utilisez la barre de navigation Twitter bootstrap twitter.github.com/bootstrap/examples/hero.html
Lee McAlilly
41
Changer pour class_name = current_page? (Link_path)? 'current': nil si vous ne voulez pas que la balise de classe s'affiche lorsqu'elle n'est pas sur la page actuelle
Matthew Hui
1
Comment modifieriez-vous cela pour les listes imbriquées utilisées dans les listes déroulantes?
doremi
9
DRY comme un désert
Orlando
1
J'ai ajouté quelques arguments supplémentaires extra_classes = nil, id = nil puis dans le content_tag content_tag (: li, class: [class_name, extra_classes], id: id) afin que vous puissiez également ajouter vos propres classes et identifiants aux éléments :)
Jon
72

Utilisez l' current_page?assistant pour déterminer si vous devez ou non attribuer la "current"classe. Par exemple:

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

Notez que vous pouvez également passer un chemin (non seulement un hachage d'options), par exemple: current_page?(root_path).

jpemberthy
la source
3
Existe-t-il un moyen pour que cela ignore les paramètres de requête?
Mohamad
@Mohamad, vous pouvez faire ceci: page_actuelle? (Contrôleur: 'utilisateurs', action: 'index')
nilid
26

J'utilise cette fonction nav_link (texte, lien) dans application_helper.rb (Rails 3) pour faire le travail et il roule mes liens de barre de navigation bootstrap twitter 2.0 pour moi.

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

Exemple:

<%=nav_link("About Us", about_path) %>
Peyton
la source
Cela pourrait être simplifié en utilisant la current_page?méthode, comme dans d'autres réponses.
Teemu Leisti
10

La façon dont je l'ai fait est d'ajouter une fonction d'assistance dans l'application_helper

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

Puis dans la navigation,

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

Cela teste le chemin du lien par rapport à l'URI de la page actuelle et renvoie votre classe actuelle ou une chaîne vide.

Je n'ai pas testé cela à fond et je suis très nouveau sur RoR (en passant après une décennie avec PHP), donc si cela a un défaut majeur, j'aimerais l'entendre.

Au moins de cette façon, vous n'avez besoin que d'une fonction d'assistance et d'un simple appel dans chaque lien.

entièrement cuit
la source
1
Juste un problème. J'ai utilisé request.path au lieu de request_uri (request_uri ne fonctionnait pas, peut-être un problème de version de rails?). Votre réponse est claire et élégante à mon avis.
Tony
6

Pour construire sur la réponse de @Skilldrick ...

Si vous ajoutez ce code à application.js, cela garantira que tous les menus déroulants avec des enfants actifs seront également marqués comme actifs ...

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

Pour récapituler le code de support> Ajoutez un assistant appelé 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

utilisé comme:

nav_link_to 'Home', root_path

qui produira du HTML comme

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

Je pense que le meilleur moyen est

application_helper.rb:

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

Et au menu:

<li class="<%= is_active('controller', 'action') %>">
IPIvliev
la source
est-il normal de laisser une classe vide "" comme ça?
Harsha MV
Oui. Cela peut sembler étrange si vous affichez la source en voyant un tas d'attributs de classe vides, mais c'est du HTML valide.
kobaltz
Vous pouvez utiliser <%= content_tag(:li, "Click me", class: is_active('controller', 'action')) %>, il n'imprimera pas un attribut de classe pour nil. apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
neonmate
4

Je sais que c'est une réponse obsolète, mais vous pouvez facilement ignorer toutes ces vérifications de page en cours en utilisant un wrapper link_to helper, appelé active_link_to gem, cela fonctionne exactement ce que vous voulez, ajoutez une classe active au lien de la page actuelle

fuyi
la source
4

Voici l'exemple complet, sur la façon d'ajouter une classe active sur la page de menu bootstrap en mode 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>
Petit Phild
la source
3

J'utilise un joyau génial appelé Tabs on Rails .

crème glacée
la source
1
Merci pour la suggestion. Étant donné que ma navigation est si simple et qu'il n'y en a qu'un avec de petits objets, la gemme serait probablement surchargée.
PeterWong
3

J'ai une version plus succincte de nav_link qui fonctionne exactement comme link_to, mais qui est personnalisée pour générer une balise li enveloppante.

Mettez ce qui suit dans votre 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 vous regardez le code ci-dessus et que vous le comparez au code link_to dans url_helper.rb, la seule différence est qu'il vérifie si l'url est la page courante et ajoute la classe "active" à une balise li d'encapsulation. C'est parce que j'utilise l'assistant nav_link avec le composant nav de Twitter Bootstrap qui préfère que les liens soient enveloppés dans des balises li et que la classe "active" soit appliquée au li externe.

La bonne chose à propos du code ci-dessus est qu'il vous permet de passer un bloc dans la fonction, comme vous pouvez le faire avec link_to.

Par exemple, une liste de navigation bootstrap avec des icônes ressemblerait à ceci:

Svelte:

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

Production:

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

De plus, tout comme l'assistant link_to, vous pouvez passer des options HTML dans nav_link, qui seront appliquées à la balise a.

Un exemple de passage d'un titre pour l'ancre:

Svelte:

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

Production:

<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
la source
Impressionnant. Pour moi, c'était l'option la plus viable, précisément parce qu'elle autorise les blocs. Merci beaucoup!
Kasperi
3

Pour moi personnellement, j'ai utilisé une combinaison de réponses ici

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

J'utilise materialize css et ma façon de rendre les principales catégories réductibles consiste à utiliser le code ci-dessous

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

j'espère que ça aide quelqu'un

Petros Kyriakou
la source
oublié ça. Merci de poster ceci comme rappel.
newdark-it
2

La current_page?méthode n'est pas assez flexible pour moi (disons que vous définissez un contrôleur mais pas une action, alors elle ne retournera true que sur l'action d'index du contrôleur), donc j'ai fait cela en fonction des autres réponses:

  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

Exemple:

nav_link_to "Pages", pages_url, :controller => 'pages'
manque de relativité
la source
Merci pour votre réponse. Je pense que la vôtre est similaire à la réponse acceptée en fait :)
PeterWong
Je suis tombé sur cette réponse via Google depuis que j'avais ce problème, alors j'ai pensé aider tous les autres qui rencontrent cela aussi :)
manque de relativité
2

Oui! Consultez cet article: Une meilleure façon d'ajouter une classe `` sélectionnée '' aux liens dans les rails

Déposez nav_link_helper.rb dans app / helpers et cela peut être aussi simple que:

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

L'assistant nav_link fonctionne exactement comme l'assistant link_to standard de Rails, mais ajoute une classe "sélectionnée" à votre lien (ou à son wrapper) si certains critères sont remplis. Par défaut, si l'url de destination du lien est la même que l'url de la page en cours, une classe par défaut «selected» est ajoutée au lien.

Il y a l'essentiel ici: https://gist.github.com/3279194

MISE À JOUR: Ceci est maintenant un joyau: http://rubygems.org/gems/nav_link_to

Dan Tello
la source
C'est sympa. J'ai pris en compte et ajouté une modification pour prendre en charge l'attribution d'un nom de classe aux éléments non sélectionnés, également, en tant que commentaire dans l'essentiel.
Teemu Leisti
2

J'utilise une aide simple comme celle-ci pour les liens de niveau supérieur afin que la /stories/my-storypage met en évidence le /storieslien

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
compliste
la source
2

Laissez-moi vous montrer ma solution:

_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
la source
1

Selon la réponse de Skilldrick , je vais le changer comme suit:

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

pour le rendre beaucoup plus utile.

Tom Chen
la source
1

Cette version est basée sur celle de @ Skilldrick mais vous permet d'ajouter du contenu html.

Ainsi, vous pouvez faire:

nav_link "A Page", a_page_path

mais aussi:

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

ou tout autre contenu html (vous pouvez ajouter une icône par exemple).

Ici, l'assistant est:

  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
la source
1

Je pense que j'ai trouvé une solution simple qui pourrait être utile dans de nombreux cas d'utilisation. Cela me permet:

  • Prend en charge non seulement le texte brut mais aussi le HTML à l'intérieur link_to (par exemple, ajoutez une icône à l'intérieur du lien)
  • Ajoutez quelques lignes de code à application_helper.rb
  • Ajouter activeau nom de classe entier de l'élément de lien au lieu qu'il s'agisse de la seule classe.

Alors, ajoutez ceci à 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

Et sur votre modèle, vous pouvez avoir quelque chose comme ceci:

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

En bonus, vous pouvez spécifier ou non un class_nameet l'utiliser comme ceci:<div class="<%= current_page?(root_path) %>">

Merci aux réponses précédentes 1 , 2 et aux ressources .

Juan Diego Gonzales
la source
0

tout cela fonctionne avec de simples barres de navigation, mais qu'en est-il du sous-menu déroulant? quand un sous-menu est sélectionné, l'élément du menu supérieur doit être rendu `` courant '' dans ce cas tabs_on_rails me soit la solution


la source
0

C'est ainsi que j'ai résolu mon projet actuel.

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

Ensuite..

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

Ma manière facile -

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

Merci.

Lwin Htoo Ko
la source
0

Si vous souhaitez également prendre en charge le hachage des options html dans la vue. Par exemple, si vous voulez l'appeler avec une autre classe CSS ou un identifiant, vous pouvez définir la fonction d'assistance comme ceci.

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

Donc, dans la vue, appelez cet assistant de la même manière que vous appelleriez link_to helper

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

Créez une méthode ApplicationHelpercomme ci-dessous.

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

Maintenant, utilisez-le dans la vue comme ci-dessous les cas d'utilisation.

1. Pour toute action d'un contrôleur spécifique

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

2. Pour toutes les actions de nombreux contrôleurs (séparés par des virgules)

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

3. Pour une action spécifique de tout contrôleur spécifique

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

4. Pour l'action spécifique de nombreux contrôleurs (séparés par des virgules)

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

5. Pour certaines actions spécifiques de tout contrôleur spécifique

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

6. Pour certaines actions spécifiques de nombreux contrôleurs (séparés par une virgule)

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

J'espère que ça aide

Lalit Kumar Maurya
la source