Rails显示深度嵌套层次结构视图的方式是什么?这似乎是一个简单的问题,但请耐心等待,也许有人会理解(并希望澄清)我的困惑。
我的应用程序是一个简单的层次结构(为了本次讨论的目的,更简化了):
- 在顶部,我有一个文档索引
- 单击其中一个,即可查看有关文档的更多详细信息,包括其章节的列表(索引)
- 单击该列表中的一章,并获得带有上一个&下一个链接和页边距中类似的章节列表,突出显示当前章节
很简单,但我的问题是通往的轨道是什么
- 嵌套资源,这样它们就不会太深
- 构建视图
我之所以问一些看似如此基本的问题,是因为从第一次点击开始,我查看的是"文档"(document#show
),还有它包含的章节(chapters#index
)。那么,应该在两个中的哪一个中构建视图呢?
下一次点击时,我会看到一章的开头。这可能是chapters#show
或pages#index
动作。不过,可以想象的是,我想它也可以(也许应该)由pages#show
处理。
想法?我是不是想得太多了?(可能)
路线
您可以这样设置路由,以利用Rails内置的嵌套资源:
resources :documents, :only => [:index, :show] do
resources :chapters, :only => :show do
resources :pages, :only => :show
end
end
这大致相当于设置这些自定义路线:
get '/documents' => 'documents#index',
:as => 'documents'
get '/documents/:id' => 'documents#show',
:as => 'document'
get '/documents/:document_id/chapters/:id' => 'chapters#show',
:as => 'document_chapter'
get '/documents/:document_id/chapters/:chapter_id/pages/:id' => 'pages#show',
:as => 'document_chapter_page'
如果URL比您希望的更长、更麻烦,或者您想使用自定义参数(例如,通过编号而不是ID识别页面),您可以随时编写自定义路由:
resources :documents, :only => [:index, :show]
get '/documents/:document_id/:id' => 'chapters#show',
:as => 'document_chapter'
get '/documents/:document_id/:chapter_id/:page' => 'pages#show',
:as => 'document_chapter_page'
有关详细信息,请参阅布线指南。
控制器
你不希望用户能够直接访问某一章,相反,他们应该看到该章的第一页。然而,可以说,章节URL在没有页码的情况下工作是有用的(尤其是如果页码指的是文档中的位置,而不是章节中的位置)。
因此,您可以从章节控制器的show
操作重定向到第一页:
class ChaptersController < ApplicationController
def show
chapter = Chapter.find(params[:id])
redirect_to [chapter.document, chapter, chapter.pages.first]
end
end
视图
有两种方法可以在不同的视图之间共享章节列表:
使用Rails内置的对渲染模型对象集合的支持。在
views/documents/show.html.erb
和views/chapters/show.html.erb
中,您可以包含以下内容(我假设每个操作都设置一个@document
变量):<ol> <%= render @document.chapters %> </ol>
Rails将查找一个名为
views/chapters/_chapter.html.erb
的局部视图,并为每个章节呈现它。它可能看起来像这样:<li><%= link_to chapter, [chapter.document, chapter] %></li>
在单个部分中共享整个列表。例如,您可以将以下内容添加到
views/documents/show.html.erb
和views/chapters/show.html.erb
:<%= render 'chapters/list', :chapters => @document.chapters %>
并创建一个名为
views/chapters/_list.html.erb
:的局部视图<ol> <% chapters.each do |chapter| %> <li><%= link_to chapter, [chapter.document, chapter] %></li> <% end %> </ol>
有关详细信息,请参见手册的"布局和渲染"部分中关于使用Partials的部分。
我不止一次读到嵌套不应该被滥用,2个级别显然很好,如果有意义的话可以是3个级别,但任何超过3个级别的东西,你可能需要重新思考。
您可以使用三个级别的嵌套,即
/docs/:id/:charge/:page
但做这种也可能是有意义的
/docs/:id/:page
即直接引用文档的页面,而不关心指定章节
如果章节是一个数字,页面是一个号码,您的路由将如何区分,即
/docs/1001/1
那是1001号文件的第1页吗?还是1001号文件的第一章?
只是在扮演魔鬼的代言人,当你试图深入挖掘时,就会显示出潜在的缺点。
我认为最自然的事情是:
'/docs' => 'docs#index' # show all docs
'/docs/:id' => 'docs#show' # show all chapters in a doc
'/docs/:id/chapter/:chpt' => 'docs#show_chapter' # show all pages in a chapter in a doc
'/docs/:id/:page' => 'docs#show' # show a page in a doc
the show actions would have:
if params[:page]
# show page specified
else
# show all chapters
end
the order of the routes is important
我建议这样做的原因是,书籍/页面查询语义可能比书籍/章节/页面更常见。
关联将是:
class Doc < ActiveRecord::Base
has_many :pages
has_many :chapters
end
我不会在路由中暴露end_page,我只会加标签?endpage=X'在任何接受:page的路由上,例如在显示操作中:
if params[:page]
if params[:end_page]
# show pages :page through :end_page
else
# show single :page
end
else
# show all chapters
end