Los rieles se vuelven parciales con bloque


119

Estoy tratando de reutilizar un componente html que he escrito que proporciona estilo de panel. Algo como:

  <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>

Entonces veo que el render toma un bloque. Entonces pensé que podría hacer algo como esto:

# /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>

Y quiero hacer algo como:

#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 %>

Desafortunadamente, esto no funciona con este error:

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 -%>
...

Entonces no le gusta el =obviamente con un bloque, pero si lo elimino, entonces simplemente no genera nada.

¿Alguien sabe cómo hacer lo que estoy tratando de lograr aquí? Me gustaría reutilizar este html de panel en muchos lugares de mi sitio.


1
La respuesta aceptada es correcta, pero desde Rails 5.0.0 esto es posible sin la layoutsolución alternativa, consulte guides.rubyonrails.org/…
fabi

Respuestas:


208

Si bien ambas respuestas anteriores funcionan (bueno, el ejemplo al que Tony enlaza de todos modos) terminé encontrando la respuesta más concisa en esa publicación anterior (comentario de Kornelis Sietsma)

Supongo que render :layouthace exactamente lo que estaba buscando:

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

combinado con:

# /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>

6
en Rails 3.2.2, creo que debe usar en <%= %>lugar de <% %>, por ejemplo<%= render :layout => '/shared/panel',
Siwei Shen 申思维

tienes razón, esta publicación no hace suposiciones sobre la versión de Rails, estoy seguro de que la gente puede darse cuenta de eso.
brad

como este (resuelve mi problema así que +1), pero ¿es una buena práctica? ¿No habrá alguna desaceleración por alguna razón? Tal vez la base de datos se active antes de lo que se supone debido a otro rendimiento (no tengo tiempo para investigar eso por mí mismo ahora, solo me pregunto)
equivalente

1
¿Alguna forma de saber si se ha producido un contenido? En caso de que sea opcional.
Vadorequest

3
En Rails 5.0 hubo un cambio, por lo que esto es general para todos los parciales, no solo para los diseños. Puede cambiar la primera línea del código de llamada a:<%= render '/shared/panel', title: 'some title' do %>
Jay Mitchell

27

Aquí hay una alternativa basada en respuestas anteriores.

Crea tu parcial en 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>

Defina su método en application_helper.rb:

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

Llámalo desde cualquier vista:

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

11

Puede usar el asistente de captura e incluso en línea en la llamada de renderizado:

<%= render 'my_partial',
           :locals => { :title => "Some Title" },
           :captured => capture { %>
    <p>Here is some content to be rendered inside the partial</p>
<% } %>

y en panel / compartido:

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

que producirá:

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

Ver http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html


1
El patrón en línea es bueno para un parcial que necesita varios bloques.
mahemoff

4

Según la respuesta aceptada, esto es lo que funcionó bien para mí usando Rails 4.

Podemos renderizar un panel como tal:

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

ingrese la descripción de la imagen aquí

Esto requiere un método auxiliar y una vista compartida:

método de ayuda (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

Ver (/ui/panel.html.haml)

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

Esto también funciona en Rails 5.x, solo necesitaba cambiar panel.html.hamla_panel.html.haml
Kris

1

Creo que funcionará (solo hice una prueba rápida y sucia) si primero la asigna a una variable y luego la genera.

<% foo = render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
<p>Here is some content to be rendered inside the panel</p>
<% end %>
<%= foo %>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.