Rails使用块渲染局部视图

138

我正在尝试重复使用我编写的提供面板样式的HTML组件。例如:

  <div class="v-panel">
    <div class="v-panel-tr"></div>
    <h3>Some Title</h3>
    <div class="v-panel-c">
      .. content goes here
    </div>
    <div class="v-panel-b"><div class="v-panel-br"></div><div class="v-panel-bl"></div></div>
  </div>

我发现render可以使用块。因此,我想我可以这样做:

# /shared/_panel.html.erb
<div class="v-panel">
  <div class="v-panel-tr"></div>
  <h3><%= title %></h3>
  <div class="v-panel-c">
    <%= yield %>
  </div>
  <div class="v-panel-b"><div class="v-panel-br"></div><div class="v-panel-bl"></div></div>
</div>

我想要做类似于:

#some html view
<%= render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
  <p>Here is some content to be rendered inside the panel</p>
<% end %>

很遗憾,这样做会出现以下错误:

ActionView::TemplateError (/Users/bradrobertson/Repos/VeloUltralite/source/trunk/app/views/sessions/new.html.erb:1: , unexpected tRPAREN

old_output_buffer = output_buffer;;@output_buffer = '';  __in_erb_template=true ; @output_buffer.concat(( render :partial => '/shared/panel', :locals => {:title => "Welcome"} do ).to_s)
on line #1 of app/views/sessions/new.html.erb:
1: <%= render :partial => '/shared/panel', :locals => {:title => "Welcome"} do -%>
...

显然,它不喜欢带有块状的=符号,但如果我去掉它,它就什么也不输出了。

有人知道我在这里尝试做什么吗?我想在我的网站上的许多地方重用这个面板html。


2
接受的答案是正确的,但自从Rails 5.0.0以来,这可以在没有“layout”变通方法的情况下实现,请参见http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials-to-simplify-views。 - fabi
5个回答

240

虽然上述两个答案都有效(至少tony链接的那个例子有效),但我最终在上面的帖子中找到了最简洁的答案(由Kornelis Sietsma评论)。

我想render :layout正是我在寻找的内容:

# Some View
<%= render :layout => '/shared/panel', :locals => {:title => 'some title'} do %>
  <p>Here is some content</p>
<% end %>

与以下内容结合使用:

# /shared/_panel.html.erb
<div class="v-panel">
  <div class="v-panel-tr"></div>
  <h3><%= title -%></h3>
  <div class="v-panel-c">
    <%= yield %>
  </div>
</div>

9
在Rails 3.2.2中,我认为你需要使用<%= %>而不是<% %>,例如:<%= render :layout => '/shared/panel' - Siwei
1
你说得对,这篇文章没有假设Rails的版本,我相信人们可以自己解决这个问题。 - brad
如果外部面板是一个“表单”(变量名为f),则应使用yield f - sequielo
1
有没有办法知道内容是否已经被yielded?如果它是可选的。 - Vadorequest
9
在Rails 5.0中引入了一项更改,所以这适用于所有的局部模板,而不仅仅是布局。您可以修改调用代码的第一行为:<%= render '/shared/panel', title: 'some title' do %> - Jay Mitchell
显示剩余3条评论

34

这里有一个基于之前答案的替代方案。

shared/_modal.html.erb中创建你的部分:

<div class="ui modal form">
  <i class="close icon"></i>
  <div class="header">
    <%= heading %>
  </div>
  <div class="content">
    <%= capture(&block) %>
  </div>
  <div class="actions">
    <div class="ui negative button">Cancel</div>
    <div class="ui positive button">Ok</div>
  </div>
</div>

application_helper.rb文件中定义你的方法:

def modal_for(heading, &block)
  render(
    partial: 'shared/modal',
    locals: { heading: heading, block: block }
  )
end

从任何视图调用它:

<%= modal_for('My Title') do |t| %>
  <p>Here is some content to be rendered inside the partial</p>
<% end %>

13
您可以使用 capture 帮助器,甚至可以在渲染调用中内联使用:
<%= render 'my_partial',
           :locals => { :title => "Some Title" },
           :captured => capture { %>
    <p>Here is some content to be rendered inside the partial</p>
<% } %>

并且在 shared/panel 中:

<h3><%= title %></h3>
<div class="my-outer-wrapper">
  <%= captured %>
</div>

这将产生:

<h3>Some Title</h3>
<div class="my-outer-wrapper">
  <p>Here is some content to be rendered inside the partial</p>
</div>

请查看http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html


2
内联模式非常适合需要多个块的部分。 - mahemoff

5

根据被接受的答案,以下是我在使用Rails 4时很好的工作方式。

我们可以这样呈现一个面板:

= render_panel('Non Compliance Reports', type: 'primary') do
  %p your content goes here!

在此输入图片描述

这需要一个帮助方法和一个共享视图:

帮助方法 (ui_helper.rb)

def render_panel(heading, options = {}, &block)
  options.reverse_merge!(type: 'default')
  options[:panel_classes] = ["panel-#{options[:type]}"]

  render layout: '/ui/panel', locals: { heading: heading, options: options } do
    capture(&block)
  end
end

查看 (/ui/panel.html.haml)

.panel{ class: options[:panel_classes] }
  .panel-heading= heading
  .panel-body
    = yield

这在Rails 5.x中也适用,我只需要将panel.html.haml更改为_panel.html.haml即可。 - Kris

1

我觉得这样做应该可以(只是进行了一个快速的测试),如果你先将它赋值给一个变量,然后再输出。

<% foo = render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
<p>Here is some content to be rendered inside the panel</p>
<% end %>
<%= foo %>

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