12

我有一个 Ruby/Rails 应用程序,它有两个或三个主要“部分”。当用户访问该部分时,我希望显示一些子导航。所有三个部分都使用相同的布局,所以我不能将导航“硬编码”到布局中。

我可以想到几种不同的方法来做到这一点。我想为了帮助人们投票,我会把它们作为答案。

还有其他想法吗?或者你投什么票?

4

9 回答 9

9

假设每个部分都有自己的控制器,您可以使用部分轻松地做到这一点。

假设您有三个部分,分别称为PostsUsersAdmin,每个部分都有自己的控制器PostsControllerUsersControllerAdminController

在每个相应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>
于 2008-10-01T13:01:46.767 回答
7
  1. 部分渲染。这与辅助方法非常相似,只是布局可能会有一些 if 语句,或者将其传递给辅助方法......
于 2008-09-29T22:46:28.827 回答
7

至于子菜单的内容,您可以在每个控制器中以声明的方式进行处理。

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 中声明一个默认(空?)menu_items。

于 2008-09-30T00:32:49.417 回答
3

警告:前方有高级技巧!

全部渲染。使用 CSS/Javascript 隐藏您不需要的那些,可以通过多种方式轻松初始化。(Javascript 可以读取使用的 URL、查询参数、cookie 中的某些内容等)这具有可能更好地使用缓存的优势(为什么缓存三个视图,然后当您可以缓存一个视图时必须同时使它们全部过期) ?),并且可以用来呈现更好的用户体验。

例如,假设您有一个带有子导航的通用标签栏界面。如果您呈现所有三个选项卡的内容(即它以 HTML 编写)并隐藏其中两个,则在两个选项卡之间切换是微不足道的 Javascript,甚至不会影响您的服务器。大胜!用户没有延迟。没有服务器负载。

想要另一个大胜利?您可以使用此技术的变体来欺骗可能在用户中 99% 常见但仍包含用户状态的页面。例如,您可能有一个网站的首页,该页面在所有用户中相对常见,但在他们登录时会说“Hiya Bob”。将非常见部分(“Hiya,Bob”)放入 cookie 中。让页面的那部分通过 Javascript 读取 cookie 被读取。 无论页面缓存中的登录状态如何,都为所有用户缓存整个页面。 这实际上能够在某些站点上从整个 Rails 堆栈中切出 70% 的访问。

当你的网站真的是 Nginx 提供静态资产时,谁在乎 Rails 是否可以扩展,新的 HTML 页面偶尔会由一些运行在每千次访问左右的 Ruby 交付;)

于 2008-09-30T08:12:52.710 回答
1

您可以在http://rpheath.com/posts/309-rails-plugin-navigation-helper使用类似导航插件的东西

它没有开箱即用的子部分导航,但稍作调整,您可能可以将其设置为执行类似的操作。

于 2008-09-30T00:06:22.787 回答
1

我建议你使用部分。有几种方法可以解决。当我创建需要特定变量的部分有点挑剔时,我还为它创建了一个辅助方法。

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 中,如果这对您更有意义的话。

于 2008-09-30T00:23:12.497 回答
1

我自己也问了几乎相同的问题:需要建议:子菜单的 Rails 视图结构?最好的解决方案可能是使用部分。

于 2008-10-01T10:16:05.027 回答
1

还有另一种可能的方法:嵌套布局

我不记得我在哪里找到了这段代码,所以向原作者道歉。

在您的 lib 文件夹中创建一个名为 nested_layouts.rb 的文件,并包含以下代码:

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 可以控制它们的位置。

于 2008-10-01T12:45:21.330 回答
0

解决这个问题的方法很少。

您可能希望为每个部分使用不同的布局。

您可能希望使用给定目录中所有视图包含的部分。

您可能希望使用content_for由视图或部分填充的视图,并在全局布局中调用(如果有的话)。

我个人认为在这种情况下你应该避免更多的抽象。

于 2008-12-26T20:08:07.897 回答