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

StackOverflow https://stackoverflow.com/questions/151066

Вопрос

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

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

Есть еще какие-нибудь идеи?Или за что вы голосуете?

Это было полезно?

Решение

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

Допустим, у вас есть три раздела, которые называются Публикации, Пользователи и Администратор, каждый со своим собственным контроллером: 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 и т.д. и т.п.) Это имеет то преимущество, что потенциально намного лучше работает с вашим кэшем (зачем кэшировать три представления, а затем истекать их все одновременно, когда вы можете кэшировать одно?), и может быть использовано для улучшения взаимодействия с пользователем.

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

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

Кого волнует, может ли 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

Теперь у partial есть локальная переменная с именем 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

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

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

include NestedLayouts

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top