Rails:如何在视图内呈现视图(不是部分视图)

44

我有一个控制器,可以同时响应htmljshtml视图将整个页面呈现出来(包括页眉和页脚),而js只会替换#main。 除了页眉和页脚之外,这两种格式呈现的内容相同。 我可以通过三个文件实现这种效果:

_show.html.erb
<div>Content!</div>

show.html.erb
<%= render "show" %>

show.js.erb
$("#main").fadeIn("<%= escape_javascript(render 'show') %>");

这个方法可行,但我希望不需要单独的_show部分。不幸的是,下面的方法不起作用:

show.html.erb
<div>Content!</div>

show.js.erb
$("#main").fadeIn("<%= escape_javascript(render 'show') %>");
作为Rails将寻找“show”局部视图而非实际视图,是否有办法让Rails查找视图文件而不是局部视图?

3
你可以使用:file选项,例如:file => 'show'。渲染辅助程序将会将render 'yourview'的字符串参数隐式转换为render :partial => 'yourview'。然而,你将遇到的问题是,在js动作中查找上下文将把"show"解析为js版本,可能导致无限循环或类似的问题。 - numbers1311407
谢天谢地,它没有进入循环。至少目前还没有。现在我得到了一个“缺少模板”的错误。看起来它甚至找不到自己。 - nullnullnull
1
在另一个视图中呈现非局部视图并不完全符合 Rails Way™。您当前的解决方案可能更好。如果您对命名感到奇怪,请将其重命名为 _body。在这种情况下,您通常会看到 render(@resource),它会转换为局部路径,例如 resources/_resource - numbers1311407
谢谢你的建议。我觉得这可能是最好的方法,但我想先听听另一个开发者的意见。随着我编写更多具有此功能的操作,长期来看可能会出现一些维护问题。但至少在短期内,我可以设置一个生成器来完成这项工作。 - nullnullnull
从前,在控制器中可以这样做,以便在JavaScript操作中获取HTML视图渲染的内容:lookup_context.update_details(:formats => [:html]) { @content = render_to_string }。然后在js视图中,您可以使用render(@content)。但是那是Rails 3.0.x,即使当时也可能不是一个干净的解决方案。 - numbers1311407
还有一种方法。虽然不是很直观,但如果没有js视图,则js操作将加载html视图。这意味着您可以仅保留show.html.erb,然后创建单独的javascript布局,甚至可以共享。例如,您可以将其称为fadein.js.erb - numbers1311407
4个回答

26

在另一个视图中呈现非局部视图不完全是Rails的方法™。你目前的解决方案可能更好,而且并不是一种不常见的方法。如果你觉得局部名称与动作相同感到奇怪,可以将其重命名为_body或其他适当的名称。

但是,如果您的视图可以共享,就像在这种情况下看起来那样,您可以将其设置为布局。

这得益于Rails有点违反最小惊讶原则的事实,即如果没有js模板,则会呈现html模板以用于js操作。这意味着您可以删除js模板和局部,只需创建一个名为fadein.js.erb 的布局:

# yourviews/show.html.erb
<div>Content!</div>

# layouts/fadein.js.erb
$("#main").fadeIn("<%= escape_javascript(yield) %>");

# YourController.rb
def show
  # ...
  respond_to do |wants|
    wants.html
    wants.js { render :layout => "fadein" }
  end
end

4
非常感谢。这个解决方案将三个文件(show.html.erbshow.js.erb_show.html.erb)合并为一个show.html.erb文件。我可以在不同的模型和操作中应用它,并且仍然只需要一个layouts/fadein.js.erb文件。太棒了! - nullnullnull
这个真的很好用!非常感谢。我只有一个问题,我无法在“创建”场景中让它工作,即表单提交后应将我重定向到刚刚创建的记录。我已经在我的form_for中添加了remote:true,但它仍然搜索create模板,尽管我已经使用respond_to为JS指向了布局JS文件。有什么建议吗?谢谢。 - Kopty
1
@Kopty 它仍然在JavaScript布局中呈现模板,作为HTML。因此,除非您告诉它不要这样做,否则它仍将像往常一样寻找创建模板。如果您想呈现show模板,可以尝试使用 render :show, :layout => "fadein" - numbers1311407
谢谢提供的提示。我尝试了,但是出现了错误:“':show'不是ActiveModel兼容的对象。它必须实现:to_partial_path。”我还尝试了硬编码控制器的show操作,但也没有成功。 - Kopty

25

就像其他人所提到的那样,在另一个视图中渲染另一个非partial视图“不是Rails的方式”,但如果您仍然坚持这样做,一种方法是:

<%= render :file => '<replace with relative/absolute path>' %>

10

你可以通过评论看出来,这不是一个好的做法。Rails有视图局部视图布局的概念。话虽如此,视图是唯一应该仅使用一次的。所以我的建议是:

  1. 如果你觉得当前视图中可以呈现多个额外视图,则很可能需要使用布局
  2. 如果你认为其中一个视图应该在多个页面中呈现,则需要使用局部视图
  3. 如果此视图只应呈现一个额外视图,并且该视图仅应在当前视图中呈现,则可以选择上述任何一个都不选 - 那就使用单个文件

8

您只需要将上下文传递给您的render方法:

<%= render :template => "show" -%>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接