如何在Ruby on Rails中实现特定于部分的导航?

时间:2008-09-29 22:45:58

标签: ruby-on-rails ruby templates actionview

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

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

还有其他想法吗?或者你投票给谁?

9 个答案:

答案 0 :(得分:9)

您可以使用partials轻松完成此操作,假设每个部分都有自己的控制器。

假设您有三个部分,分别称为帖子用户管理员,每个部分都有自己的控制器:PostsController,{ {1}}和UsersController

在每个相应的AdminController目录中,您声明views部分:

/app/views/users/_subnav.html.erb
/app/views/posts/_subnav.html.erb
/app/views/admin/_subnav.html.erb

在每个子区域部分中,您声明了特定于该部分的选项,因此_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

最后,一旦你完成了这个,你只需要在布局中包含subnav partial:

<ul id="subnav">
  <li><%= link_to 'All Posts', posts_path %></li>
  <li><%= link_to 'New Post', new_post_path %></li>
</ul>

答案 1 :(得分:7)

  1. 部分渲染。这与helper方法非常相似,除了布局可能有一些if语句,或者将它传递给一个帮助器......

答案 2 :(得分: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。

答案 3 :(得分:3)

警告:高级技巧提前!

全部渲染。使用CSS / Javascript隐藏您不需要的内容,可以通过多种方式进行简单的初始化。 (Javascript可以读取使用的URL,查询参数,cookie中的某些内容等等)这样做的好处是可以更好地使用缓存(为什么缓存三个视图,然后在缓存一个时必须同时使它们全部到期) ?),可用于呈现更好的用户体验。

例如,让我们假装您有一个带子导航的常用标签栏界面。如果您渲染所有三个选项卡的内容(即它在HTML中编写)并隐藏其中两个选项卡,则在两个选项卡之间切换是微不足道的Javascript,而甚至不会命中您的服务器。大赢!没有用户延迟。没有服务器负载。

想要又一场大胜?您可以使用此技术的变体来欺骗页面,这些页面可能只有99%在用户之间共同但仍包含用户状态。例如,您可能拥有一个站点的首页,该站点在所有用户中相对较常见,但在登录时会说“Hiya Bob”。将非公共部分(“Hiya,Bob”)放入cookie中。通过阅读cookie的Javascript读取页面的那一部分。 在页面缓存中为所有用户缓存整个页面而不管登录状态这实际上能够在某些站点上从整个Rails堆栈中切除70%的访问权限。

当你的网站真的是Nginx服务静态资产时,谁会关心Rails是否可以扩展,新的HTML页面偶尔会被一些运行在每千次访问中的Ruby所提供;)

答案 4 :(得分:1)

您可以在http://rpheath.com/posts/309-rails-plugin-navigation-helper

使用类似导航插件的内容

它没有开箱即用的子部分导航,但稍微调整一下就可以设置它来做类似的事情。

答案 5 :(得分: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中,如果这对你更有意义。

答案 6 :(得分:1)

我自己也问过同样的问题:Need advice: Structure of Rails views for submenus?最好的解决办法可能就是使用偏见。

答案 7 :(得分: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

然后,在layouts文件夹中创建各种布局(例如'admin.rhtml'和'application.rhtml')。

现在在您的控制器中,只需在类中添加它:

include NestedLayouts

最后在你的行动结束时这样做:

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

数组中布局的顺序很重要。管理员布局将在“yeild”所在的应用程序布局中呈现。

这种方法可以很好地工作,具体取决于网站的设计以及各种元素的组织方式。例如,其中一个包含的布局可能只包含一系列包含需要为特定操作显示的内容的div,而更高布局上的CSS可以控制它们的位置。

答案 8 :(得分:0)

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

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

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

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

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