Как реализовать навигацию по разделам в Ruby on Rails?

У меня есть приложение Ruby / Rails, которое имеет два или три основных "раздела". Когда пользователь посещает этот раздел, я хочу отобразить некоторую поднавигацию. Все три раздела используют один и тот же макет, поэтому я не могу "жестко кодировать" навигацию по макету.

Я могу придумать несколько разных методов для этого. Я думаю, чтобы помочь людям проголосовать, я поставлю их в качестве ответов.

есть другие идеи? Или за что вы голосуете?

9 ответов


вы можете легко сделать это с помощью partials, предполагая, что каждый раздел имеет свой собственный контроллер.

скажем, у вас есть три раздела под названием посты, пользователи и Admin, каждый со своим собственным контроллером:PostsController, UsersController и AdminController.

в каждом соответствующем views каталог, вы объявляете _subnav.html.erb фрагмент:

/app/views/users/_subnav.html.erb
/app/views/posts/_subnav.html.erb
/app/views/admin/_subnav.html.erb

в каждом из этих частичных subnav вы объявляете параметры, специфичные для этого раздел, так /users/_subnav.html.erb может содержать:

<ul id="subnav">
  <li><%= link_to 'All Users', users_path %></li>
  <li><%= link_to 'New User', new_user_path %></li>
</ul>

пока /posts/_subnav.html.erb может содержать:

<ul id="subnav">
  <li><%= link_to 'All Posts', posts_path %></li>
  <li><%= link_to 'New Post', new_post_path %></li>
</ul>

наконец, как только вы это сделаете, вам просто нужно включить частичный subnav в макет:

<div id="header">...</div>    
<%= render :partial => "subnav" %>
<div id="content"><%= yield %></div>
<div id="footer">...</div>

  1. частичная отрисовка. Это очень похоже на вспомогательный метод, за исключением, возможно, макета будет иметь некоторые операторы if или передать это помощнику...

что касается содержимого вашего подменю, вы можете перейти к нему декларативным образом в каждом контроллере.

class PostsController < ApplicationController
#...
protected
  helper_method :menu_items
  def menu_items
    [
      ['Submenu 1', url_for(me)],
      ['Submenu 2', url_for(you)]
    ]
  end
end

теперь, когда вы вызываете menu_items из представления, у вас будет правильный список для итерации для конкретного контроллера.

Это кажется мне более чистым решением, чем поместить эту логику внутри шаблонов представлений.

обратите внимание, что вы также можете объявить значение по умолчанию (пустое?) menu_items внутри ApplicationController также.


предупреждение: дополнительные трюки впереди!

сделать их всех. Скройте те, которые вам не нужны, используя CSS / Javascript, которые могут быть тривиально инициализированы любым количеством способов. (Javascript может читать используемый URL, параметры запроса, что-то в файле cookie и т. д.) Это имеет то преимущество, что потенциально играть гораздо лучше с вашим кэшем (почему кэшировать три представления, а затем должны истекать их все одновременно, когда вы можете кэшировать один?), и может быть использовано предоставить лучший пользовательский опыт.

давайте представим, что у вас есть общие вкладки интерфейс с Sub навигации. Если вы визуализируете содержимое всех трех вкладок (т. е. написанное в HTML) и скрываете два из них, переключение между двумя вкладками является тривиальным Javascript и даже не попадает на ваш сервер. Большая победа! Нет задержки для пользователя. Нет загрузки сервера для вас.

хочу еще один большой выигрыш? Вы можете использовать вариант этот метод для обмана на страницах, которые могут быть только 99% распространены среди пользователей, но все еще содержат состояние пользователя. Например, у вас может быть первая страница сайта, которая относительно распространена среди всех пользователей, но говорит "Hiya Bob", когда они вошли в систему. Положите не общую часть ("Привет, Боб") в печенье. Пусть эта часть страницы будет прочитана через Javascript, читая cookie. кэшировать всю страницу все пользователи независимо от статуса входа в кэширование страниц. Это буквально способен отрезать 70% доступа от всего стека рельсов на некоторых сайтах.

кого волнует, могут ли Rails масштабироваться или нет, когда ваш сайт действительно nginx обслуживает статические активы с новыми HTML-страницами, иногда доставляемыми каким-то Ruby, работающим на каждом тысячном доступе или около того ;)


вы можете использовать что-то вроде плагина навигации вhttp://rpheath.com/posts/309-rails-plugin-navigation-helper

Это не суб-раздел навигация "из коробки", но с небольшими изменениями вы могли бы настроить его, чтобы сделать что-то подобное.


Я предлагаю вам использовать частичные. Есть несколько способов сделать это. Когда я создаю партиалы, которые немного придирчивы в том, что им нужны определенные переменные, я также создаю вспомогательный метод для него.

module RenderHelper
  #options: a nested array of menu names and their corresponding url
  def render_submenu(menu_items=[[]])
    render :partial => 'shared/submenu', :locals => {:menu_items => menu_items}
  end
end

теперь частичный имеет локальную переменную с именем menu_items, над которой вы можете выполнить итерацию для создания подменю. Обратите внимание, что я предлагаю вложенный массив вместо хэша, потому что порядок хэша непредсказуем.

обратите внимание, что логика, решающая, какие элементы должны быть отображаемое в меню также может быть внутри render_submenu, если это имеет для вас больше смысла.


Я сам задал почти тот же вопрос:нужен совет: структура представлений Rails для подменю? лучшим решением, вероятно, было использование частичных чисел.


есть еще один возможный способ сделать это: вложенные макеты

Я не помню, где я нашел этот код, так что приношу извинения автору.

создайте файл с именем nested_layouts.rb в папке lib и включите следующий код:

module NestedLayouts
  def render(options = nil, &block)
    if options
      if options[:layout].is_a?(Array)
        layouts = options.delete(:layout)
        options[:layout] = layouts.pop
        inner_layout = layouts.shift
        options[:text] = layouts.inject(render_to_string(options.merge({:layout=>inner_layout}))) do |output,layout|
          render_to_string(options.merge({:text => output, :layout => layout}))
        end
      end
    end
    super
  end
end

затем создайте различные макеты в папке "макеты" (например, " admin.rhtml " и " применение.файлы rhtml').

Теперь в ваших контроллерах добавьте это только внутри класс:

include NestedLayouts

и, наконец, в конце ваших действий этого:

def show
  ...
  render :layout => ['admin','application']
end

порядок слоев в массиве имеет важное значение. Макет администратора будет отображаться внутри макета приложения, где бы ни находился "yeild".

этот метод может работать очень хорошо, в зависимости от дизайна сайта и как различные элементы организованы. например, один из включенных макетов может содержать только серию divs, которые содержат контент, который должен быть показано для определенного действия, и CSS на более высоком макете может контролировать, где они расположены.


Существует несколько подходов к этой проблеме.

вы можете использовать различные макеты для каждого раздела.

вы можете использовать частичный, включенный всеми представлениями в данном каталоге.

вы можете использовать content_for Это заполняется либо видом, либо частичным и вызывается в глобальном макете, если он у вас есть.

лично я считаю, что в этом случае вам следует избегать большей абстракции.