Как реализовать навигацию по отдельным разделам в Ruby on Rails?
У меня есть приложение Ruby/Rails с двумя или тремя основными разделами. Когда пользователь посещает этот раздел, я хочу отобразить некоторую суб-навигацию. Все три раздела используют один и тот же макет, поэтому я не могу "жестко закодировать" навигацию в макете.
Я могу придумать несколько разных способов сделать это. Думаю, чтобы помочь людям голосовать, я отведу их в качестве ответов.
Любые другие идеи? Или за что вы проголосовали?
Ответы
Ответ 1
Вы можете легко сделать это с помощью partials, считая, что в каждой секции есть собственный контроллер.
Скажем, у вас есть три раздела: Сообщения, Пользователи и Администратор, каждый из которых имеет собственный контроллер: PostsController
, UsersController
и AdminController
.
В каждом соответствующем каталоге views
вы объявляете _subnav.html.erb
partial:
/app/views/users/_subnav.html.erb
/app/views/posts/_subnav.html.erb
/app/views/admin/_subnav.html.erb
В каждом из этих subnav partials вы объявляете параметры, специфичные для этого раздела, поэтому /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>
Ответ 2
- Частичный рендеринг. Это очень похоже на вспомогательный метод, за исключением того, что в макете будут какие-то операторы if или передать это помощнику...
Ответ 3
Что касается содержимого ваших подменю, вы можете использовать его декларативно в каждом контроллере.
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.
Ответ 4
Предупреждение: продвинутые трюки впереди!
Отдайте их всем. Скройте те, которые вам не нужны, используя CSS/Javascript, которые могут быть тривиально инициализированы любым количеством способов. (Javascript может читать URL-адрес, параметры запроса, что-то в файле cookie и т.д.). Это имеет то преимущество, что вы потенциально гораздо лучше играете с кешем (зачем кешировать три представления, а затем они должны истекать одновременно, когда вы можете кэшировать один?), и его можно использовать для обеспечения лучшего пользовательского опыта.
Например, предположим, что у вас есть общий интерфейс панели вкладок с суб-навигацией. Если вы разместите содержимое всех трех вкладок (т.е. Написано в HTML) и спрячете два из них, переключение между двумя вкладками - тривиальный Javascript, а даже не попадет на ваш сервер. Большая победа! Отсутствие задержки для пользователя. Нет загрузки сервера для вас.
Хотите еще одну большую победу? Вы можете использовать вариант этой методики, чтобы обманывать страницы, которые могут быть 99% распространены среди пользователей, но все еще содержат состояние пользователя. Например, у вас может быть первая страница сайта, которая относительно распространена среди всех пользователей, но скажет "Hiya Bob", когда они вошли в систему. Поместите не общую часть ( "Hiya, Bob" ) в файл cookie. Прочитайте эту часть страницы с помощью Javascript, читающего файл cookie. Кэш всей страницы для всех пользователей, независимо от состояния входа в кеширование страниц.. Это буквально способно разрезать 70% доступа от всего стека Rails на некоторых сайтах.
Кого волнует, может ли Rails масштабироваться или нет, когда ваш сайт действительно Nginx, обслуживающий статические активы с новыми HTML-страницами, иногда получая какой-то Ruby, работающий на каждый тысячный доступ или так;)
Ответ 5
Вы можете использовать что-то вроде плагина навигации в http://rpheath.com/posts/309-rails-plugin-navigation-helper
Это не делает навигацию подзаголовка из коробки, но с небольшой настройкой вы, вероятно, можете настроить ее на выполнение чего-то подобного.
Ответ 6
Я предлагаю вам использовать частичные. Есть несколько способов, которыми вы можете это сделать. Когда я создаю частичные, которые немного придирчивы к тому, что им нужны конкретные переменные, я также создаю для него вспомогательный метод.
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, если это имеет для вас больше смысла.
Ответ 7
Я сам задал почти тот же вопрос: Нужен совет: Структура представлений Rails для подменю? Лучшим решением, вероятно, было использование частичных файлов.
Ответ 8
Существует еще один способ сделать это: Вложенные макеты
Я не помню, где я нашел этот код, поэтому извиняюсь перед оригинальным автором.
создайте файл 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" и "application.rhtml" ).
Теперь в ваших контроллерах добавьте это только внутри класса:
include NestedLayouts
И, наконец, в конце ваших действий сделайте следующее:
def show
...
render :layout => ['admin','application']
end
порядок макетов в массиве важен. Макет администратора будет отображаться внутри макета приложения везде, где есть "yeild".
этот метод может работать очень хорошо в зависимости от дизайна сайта и того, как организованы различные элементы. например, один из включенных макетов может содержать только серию div, которые содержат контент, который должен быть показан для определенного действия, а CSS на более высоком макете может контролировать, где они расположены.
Ответ 9
Существует несколько подходов к этой проблеме.
Возможно, вы захотите использовать разные макеты для каждого раздела.
Возможно, вы захотите использовать частичное включение всех представлений в данном каталоге.
Возможно, вы захотите использовать content_for
, который заполняется либо представлением, либо частичным, и вызывается в глобальном макете, если он у вас есть.
Лично я считаю, что вам следует избегать большей абстракции в этом случае.