ナビゲーションメニューのclassどう書くべきか

ナビゲーションメニューのリンクの所でこういうのをいつも書くんですが、とても見辛くて良い方法が思いつきません。

<ul>
  <li><%= link_to "Listing Users", users_path, class: "global-nav #{controller_name == "users" ? "active" : "inactive"}" %></li>
  <li><%= link_to "Listing Posts", posts_path, class: "global-nav #{controller_name == "posts" ? "active" : "inactive"}" %></li>
</ul>

CSSでのデザイン上、link_to_ifとかでは駄目なようです。
皆さんはどう書いていますか?

「いいね!」 1
def li_link(active, body, url, html_options={})
  content_tag(:li, link_to(body, url, html_options), class: active ? 'active' : '')
end

こんなヘルパーを

= li_link controller_name == 'foos', Foo.model_name.human, foos_path
= li_link controller_name == 'bars', Bar.model_name.human, bars_path

こんな感じで使ってるんですが、メソッド名もいいの思いつかなくていつも気持ち悪い感じになっています。

「いいね!」 2
def active_if(*names)
	"active" if active_namespace?(*names) || active_controller?(*names) || active_action?(*names)
	"inactive"
end

def active_controller?(*names)
  names.any? { |name| controller_path == name }
end

def active_namespace?(*namespacs)
  namespaces.any? { |namespace|  controller_path.start_with?(namespace) }
end

def active_action?(*names)
  names.any? { |name| "#{controller_path}##{action_name}" == name }
end
<ul>
  <li><%= link_to "Listing Users", users_path, class: "global-nav #{active_if('user')}" %></li>
  <li><%= link_to "Listing Posts", posts_path, class: "global-nav #{active_if('posts')}" %></li>
</ul>

大体いつもactiveにする要件が増えて、ぐちゃぐちゃになるんで、
名前空間でマッチさせる時と、controller名でマッチさせる時と、
controller/actionでマッチさせる時が必要になる印象があります。

*name とすることで、複数の要件でactiveにしておくみたいなのもクリアできます。

あと余談かもしれませんが、inactiveはつけなくてもいいようにcss設計で逃げたいなぁと。。。

ローカル変数のハッシュを定義してそれをbooleanをキーに参照するのはどうでしょう。

<% global_nav_css = {true => "global-nav active", false => "global-nav inactive"} %>
<ul>
  <li><%= link_to "Listing Users", users_path, class: global_nav_css[controller_name == "users"] %></li>
  <li><%= link_to "Listing Posts", posts_path, class: global_nav_css[controller_name == "posts"] %></li>
</ul>

他の方法に比べると以下のような利点があると思います。

  • 式展開がない
  • ローカル変数なので他のテンプレートファイルに一切影響を与えない
  • 定義と使うところが近いのでコードを追いやすい

ナビゲーションメニューのような比較的静的なCSSのクラス名を動的に作るのは無駄がありそうだし、ヘルパーを定義すると影響範囲がかなり大きくなってしまうし、頑張って一般化したヘルパー定義するのはかなりしんどい気がします。

特定のメニュー固有のクラスを付けたければ以下のように単に足せばよさそうです

<% global_nav_css = {true => "global-nav active", false => "global-nav inactive"} %>
<ul>
  <li><%= link_to "Listing Users", users_path, class: global_nav_css[controller_name == "users"] + " global-nav--new" %></li>
  <li><%= link_to "Listing Posts", posts_path, class: global_nav_css[controller_name == "posts"] %></li>
</ul>

controller_name == "users"inquiryしてやるとusers?とかposts?とか書けそうですね

<% controler_name = self.controler_name.inquiry %>
<% global_nav_css = {true => "global-nav active", false => "global-nav inactive"} %>
<ul>
  <li><%= link_to "Listing Users", users_path, class: global_nav_css[controler_name.users?] %></li>
  <li><%= link_to "Listing Posts", posts_path, class: global_nav_css[controller_name.posts?] %></li>
</ul>
「いいね!」 1

仕様にもよりますが、自分はactive_link_toというgemで済ませることがあります。
inactiveを付与したことはないのですがオプションでつけることができるっぽいので、多分以下のように書くことができると思います

<ul>
  <li><%= active_link_to "Listing Users", users_path, class: "global-nav", class_inactive: 'inactive %></li>
  <li><%= active_link_to "Listing Posts", posts_path, class: "global-nav", class_inactive: 'inactive %></li>
</ul>
「いいね!」 2

Hamlで書きます。

%ul                                                                                                                                                                                                         
  %li                                                                                                                                                                                                       
    = link_to_if controller_name == "users", "Listing Users", users_path, class: "global-nav active" do                                                                                                      
      = link_to "Listing Users", users_path, class: "global-nav inactive" 

ブロックを使えばlink_to_ifで書くこともできますが、どうでしょうか。