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





Вы можете использовать что-то вроде плагина навигации по адресу 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, если это имеет для вас больше смысла.
Что касается содержания ваших подменю, вы можете декларативно работать с ним в каждом контроллере.
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 из представления, у вас будет правильный список для перебора конкретного контроллера.
Это кажется мне более чистым решением, чем размещение этой логики внутри шаблонов представлений.
Обратите внимание, что вы также можете объявить элементы меню по умолчанию (пустые?) Внутри ApplicationController.
Не лучше ли иметь текст для параметров подменю в представлении, а не в контроллере?
Это худший из возможных подходов. Как указывалось выше, он полностью нарушает MVC.
Предупреждение: впереди продвинутые трюки!
Сделайте их все. Скройте те, которые вам не нужны, с помощью CSS / Javascript, которые можно тривиально инициализировать любым количеством способов. (Javascript может читать используемый URL-адрес, параметры запроса, что-то в файле cookie и т. д. И т. Д.) Это имеет то преимущество, что потенциально может намного лучше работать с вашим кешем (зачем кешировать три представления, а затем истекать их все одновременно, если вы можете кэшировать один ?), и может использоваться для улучшения взаимодействия с пользователем.
Например, давайте представим, что у вас есть общий интерфейс панели вкладок с суб-навигацией. Если вы визуализируете содержимое всех трех вкладок (т.е. оно написано в HTML) и скрываете две из них, переключение между двумя вкладками выполняется тривиально: Javascript и даже не попадает в ваш сервер. Большая победа! Нет задержки для пользователя. Никакой нагрузки на сервер для вас.
Хотите еще одна большая победа? Вы можете использовать вариант этого метода для обмана страниц, которые могут быть не более чем на 99% общими для пользователей, но все же содержать пользовательское состояние. Например, у вас может быть первая страница сайта, которая является относительно общей для всех пользователей, но при входе в систему произносится «Привет, Боб». Поместите необщую часть («Привет, Боб») в файл cookie. Прочтите эту часть страницы через Javascript, читающий файл cookie. Кэшировать всю страницу для все пользователи независимо от статуса входа при кэшировании страницы. Это буквально способно отрезать 70% доступа от всего стека Rails на некоторых сайтах.
Кого волнует, может ли Rails масштабироваться или нет, когда ваш сайт на самом деле является Nginx, обслуживающим статические ресурсы, с новыми HTML-страницами, которые иногда доставляются каким-то Ruby, работающим при каждом тысячном доступе или около того;)
Обратите внимание, что этот ответ воля не удастся для некоторых пользователей, если вы сначала не протестируете (или не предположите) JavaScript или файлы cookie, соответственно. Лучшим ответом было бы использовать вместо этого JavaScript с частичным HTML или Varnish + ESI.
Я сам задал примерно тот же вопрос: Нужен совет: структура представлений 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 и application.rhtml).
Теперь в ваших контроллерах добавьте это внутри класса:
include NestedLayouts
И, наконец, в конце ваших действий сделайте следующее:
def show
...
render :layout => ['admin','application']
end
порядок макетов в массиве важен. Макет администратора будет отображаться внутри макета приложения, где бы ни находился «результат».
этот метод может очень хорошо работать в зависимости от дизайна сайта и того, как организованы различные элементы. например, один из включенных макетов может просто содержать серию блоков div, содержащих контент, который необходимо отобразить для определенного действия, а CSS в макете более высокого уровня может контролировать их расположение.
Вы можете легко сделать это с помощью частичных файлов, если у каждого раздела есть собственный контроллер.
Допустим, у вас есть три раздела с именами Сообщения, Пользователи и Админ, каждый со своим собственным контроллером: 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
В каждой из этих частей субнава вы объявляете параметры, относящиеся к этому разделу, поэтому /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>
Есть несколько подходов к этой проблеме.
Возможно, вы захотите использовать разные макеты для каждого раздела.
Возможно, вы захотите использовать партиал, включенный всеми представлениями в данном каталоге.
Возможно, вы захотите использовать content_for, который заполняется представлением или частичным и вызывается в глобальном макете, если он у вас есть.
Лично я считаю, что в этом случае вам следует избегать большей абстракции.
да, что-то вроде render: partial => 'section1_subnav'