web-dev-qa-db-fra.com

Meilleure façon d’ajouter une classe "actuelle" à la navigation dans Rails 3

J'ai des pages statiques dans un menu de navigation. Je souhaite ajouter une classe du type "current" à l'élément en cours d'affichage.

Pour ce faire, j'ajoute des tonnes de méthodes d'assistance (chacune pour un élément) afin de contrôler 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 ......

110
PeterWong

Ce n'est pas vraiment une réponse ici, parce que j'utilise la même chose 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…

55
Yannis

J'ai créé 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 HTML comme

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

Utilisez le fichier current_page? pour déterminer si vous devez ou non affecter la classe "current". Par exemple: 

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

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

68
jpemberthy

J'utilise cette fonction nav_link (text, link) dans application_helper.rb (Rails 3) pour faire le travail et me lance les liens de la barre de navigation de mon navigateur Twitter 2.0. 

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) %>
26
Peyton

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

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

Puis dans le nav, 

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

Ceci teste le chemin du lien avec la page en cours uri et retourne soit votre classe actuelle, soit une chaîne vide.

Je n'ai pas testé cela à fond et je connais très bien RoR (après une décennie d'utilisation de PHP). Si cela présente un défaut majeur, j'aimerais l'entendre.

Au moins, vous n’avez besoin que d’une fonction d’aide et d’un simple appel dans chaque lien.

10
fullybaked

Pour construire 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 du code positif> Ajoutez un utilitaire 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 HTML comme

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

Je sais que c'est une réponse périmée, mais vous pouvez facilement ignorer toutes ces vérifications de page en cours en utilisant un wrapper helper link_to, appelé active_link_to gem, il fonctionne exactement comme vous le souhaitez, ajoutez une classe active au lien de la page en cours.

4
fuyi

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 dans le menu:

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

Voici l'exemple complet sur l'ajout d'une classe active sur la page du menu d'amorçage dans la vue 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>
4
Little Phild

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

3
icecream

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 Materialise CSS et ma façon de rendre les principales catégories réductibles est d'utiliser le code ci-dessous

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

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

3
Petros Kyriakou

La méthode current_page? n'est pas assez souple pour moi (disons que vous définissez un contrôleur mais pas une action, alors il ne retournera que vrai sur l'action d'index du contrôleur), j'ai donc 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'
2
unrelativity

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 examinez le code ci-dessus et le comparez au code link_to de url_helper.rb, la seule différence est qu'il vérifie si l'URL est la page en cours et ajoute la classe "active" à une balise li enveloppante. Ceci est dû au fait que j'utilise l'aide de nav_link avec le composant nav de Twitter Bootstrap, qui préfère que les liens soient placés dans des balises li et que la classe "active" soit appliquée à la liste externe.

La bonne chose à propos du code ci-dessus est qu’il vous permet de passer un bloc à 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

Sortie:

<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 sera appliqué à la balise a.

Un exemple de passage dans 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

Sortie:

<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>
2
Joe Chen

Laissez-moi 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
2
Mike Andrianov

J'utilise un assistant simple comme celui-ci pour les liens de haut niveau afin que la page /stories/my-story mette en surbrillance le lien /stories

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
complistic

Oui! Découvrez cet article: Une meilleure façon d’ajouter une classe ‘sélectionnée’ aux liens dans 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 comme l'assistant standard Rails link_to, 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 celle de la page en cours, une classe par défaut de "sélectionné" est ajoutée au lien.

Il y a un Gist ici: https://Gist.github.com/3279194

UPDATE: Ceci est maintenant un joyau: http://rubygems.org/gems/nav_link_to

2
Dan Tello

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'aide 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
1

Selon le réponse de Skilldrick , je le changerai 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.

1
Tom Chen

Je pense avoir proposé une solution simple qui pourrait être utile dans de nombreux cas d'utilisation. Cela me permet de:

  • Prise en charge non seulement du texte brut, mais également du code HTML dans link_to (par exemple, ajouter une icône dans le lien)
  • Ajoutez seulement quelques lignes de code à application_helper.rb
  • Ajoutez active au nom de classe entier de l'élément link au lieu d'être la classe unique.

Donc, 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 tant que bonus, vous pouvez spécifier ou non un class_name et l'utiliser comme ceci: <div class="<%= current_page?(root_path) %>">

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

1

Voici comment 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
0
GN.

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

0
Lwin Htoo Ko

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 ou identifiant CSS, 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 façon que vous appelleriez link_to helper

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

Créez une méthode dans ApplicationHelper comme 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 comme indiqué ci-dessous.

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 une virgule)

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

3. Pour une action spécifique d'un 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 une virgule)

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

5. Pour certaines actions spécifiques d'un 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

0
Lalit Kumar

tout cela fonctionne avec de simples barres de navigation, mais qu'en est-il du sous-menu déroulant? lorsqu'un sous-menu est sélectionné, l'élément du menu supérieur doit être défini comme "actuel"

0
user762579