Подтвердить что ты не робот

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

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

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

Любые другие идеи? Или за что вы проголосовали?

4b9b3361

Ответ 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

  1. Частичный рендеринг. Это очень похоже на вспомогательный метод, за исключением того, что в макете будут какие-то операторы 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, если это имеет для вас больше смысла.

Ответ 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, который заполняется либо представлением, либо частичным, и вызывается в глобальном макете, если он у вас есть.

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