Rails render parziale con block


119

Sto cercando di riutilizzare un componente html che ho scritto che fornisce lo stile del pannello. Qualcosa di simile a:

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

Quindi vedo che il rendering richiede un blocco. Ho pensato che avrei potuto fare qualcosa del genere:

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

E voglio fare qualcosa come:

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

Purtroppo questo non funziona con questo errore:

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

Quindi non gli piace =ovviamente con un blocco, ma se lo rimuovo, non emette nulla.

Qualcuno sa come fare quello che sto cercando di ottenere qui? Vorrei riutilizzare questo pannello html in molti punti del mio sito.


1
La risposta accettata è corretta, ma dal momento che Rails 5.0.0 questo è possibile senza l' layout-workaround, vedere guides.rubyonrails.org/...
Fabi

Risposte:


208

Mentre entrambe le risposte sopra funzionano (beh l'esempio a cui tony si collega comunque) ho finito per trovare la risposta più succinta in quel post sopra (commento di Kornelis Sietsma)

Immagino che render :layoutfaccia esattamente quello che stavo cercando:

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

combinata 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
in Rails 3.2.2, penso che tu debba usare al <%= %>posto di <% %>, ad esempio<%= render :layout => '/shared/panel',
Siwei Shen 申思维

hai ragione, questo post non fa supposizioni sulla versione Rails, sono sicuro che le persone possono capirlo.
brad

come questo (risolve il mio problema quindi +1), ma è una buona pratica? non ci sarà qualche rallentamento per qualche motivo? Forse il database si attiva prima di quanto si supponga a causa di un altro rendimento (non ho tempo per indagare da solo ora, mi chiedo solo)
equivalente

1
Qualche modo per sapere se un contenuto è stato ceduto? In caso è facoltativo.
Vadorequest

3
In Rails 5.0 c'è stato un cambiamento, quindi questo è generale per tutti i partial, non solo per i layout. È possibile modificare la prima riga del codice di chiamata in:<%= render '/shared/panel', title: 'some title' do %>
Jay Mitchell,

27

Ecco un'alternativa basata sulle risposte precedenti.

Crea il tuo parziale su 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>

Definisci il tuo metodo su application_helper.rb:

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

Chiamalo da qualsiasi vista:

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

11

Puoi utilizzare l'helper di acquisizione e anche inline nella chiamata di rendering:

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

e in shared / panel:

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

che produrrà:

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

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


1
Il pattern inline è utile per un parziale che necessita di più blocchi.
mahemoff

4

Sulla base della risposta accettata, questo è ciò che ha funzionato bene per me usando Rails 4.

Possiamo rendere un pannello come tale:

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

inserisci qui la descrizione dell'immagine

Ciò richiede un metodo di supporto e una vista condivisa:

metodo di supporto (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

Visualizza (/ui/panel.html.haml)

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

Funziona anche in Rails 5.x, dovevo solo passare panel.html.hamla_panel.html.haml
Kris

1

Penso che funzionerà (ho appena eseguito un test rapido e sporco) se lo assegni prima a una variabile e poi lo visualizzi.

<% foo = render :partial => '/shared/panel', :locals =>{:title => "Some Title"} do %>
<p>Here is some content to be rendered inside the panel</p>
<% end %>
<%= foo %>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.