Bagaimana cara membuat sebagian dari format yang berbeda di Rails?

114

Saya mencoba menghasilkan respons JSON yang menyertakan beberapa HTML. Jadi, saya punya/app/views/foo/bar.json.erb :

{
  someKey: 'some value',
  someHTML: "<%= h render(:partial => '/foo/baz') -%>"
}

Saya ingin merender /app/views/foo/_baz.html.erb, tetapi hanya akan merender /app/views/foo/_baz.json.erb. Melewati :format => 'html'tidak membantu.

James A. Rosen
sumber
Judul pertanyaan jauh lebih umum daripada masalah khusus James. Jika Anda mencari solusi lain, lihat di sini: stackoverflow.com/questions/7616097/…
miguelsan

Jawaban:

100

Dimulai dengan Rails 3.2.3, saat memanggil render: parsial (hanya bekerja di luar respond_toblok).

render formats: [ :html ]

dari pada

render format: 'html'
Tim Haines
sumber
Saya kira mungkin ada jawaban "terbaik" yang berbeda tergantung pada konteksnya, jadi sulit untuk memberi suara positif pada pertanyaan ini, tetapi ini memenuhi kebutuhan saya untuk dengan mudah merender template HTML dari dalam RJS salah satu nama yang sama setelah berharap bahwa :formatopsi akan kerja. Terima kasih!
ches
1
Ini juga berfungsi untuk Rails 5.0.2. Namun, perhatikan bahwa Anda mungkin juga perlu menentukan :content_typeopsi dalam beberapa kasus - mis. Merender template HTML dari dalam format.pdfblok hanya dapat dilakukan dengan render "template", formats: [:html], content_type: "text/html". Saya memerlukan ini karena saya mengizinkan pengunduhan langsung contoh buku saya hanya untuk anggota milis saya - pengunjung biasa mendapatkan formulir pendaftaran sebagai gantinya.
Michael Trojanek
64

ada apa dengan

render :partial => '/foo/baz.html.erb'

? Saya baru saja mencoba ini untuk membuat sebagian ERB HTML dari dalam template pembuat Atom dan berfungsi dengan baik. Tidak perlu mengotak-atik variabel global yang diperlukan (ya, saya tahu mereka memiliki "@" di depannya, tapi itulah mereka).

with_format &blockPendekatan Anda keren, dan memiliki keuntungan bahwa Anda hanya menentukan formatnya, sedangkan pendekatan sederhana menentukan mesin template (ERB / ​​builder / etc) juga.

Sam Stokes
sumber
26
Satu-satunya downside dari hal ini adalah jika sebagian Anda membuat sebagian lainnya, itu akan gagal kecuali Anda masuk dan mengubah semua panggilan parsial render Anda untuk menyertakan ".html.erb" pada namanya.
chrisrbailey
4
Anda tidak perlu menentukan mesin template agar ini berfungsi. (Setidaknya pada rel 3). Berikut ini bekerja dengan baik: render (: partial => "baz.html")
Tim Harper
1
Tidak berfungsi jika Anda memiliki parsial yang berbeda untuk lokal yang berbeda (misalnya baz.en.html.erb, baz.fr.html.erb) dan Anda menginginkan render :partiallogika untuk memilih yang benar (dengan fallback, dll).
Yohanes
1
Pada rel 3.2.3, saya melihat peringatan berikut saat menggunakan solusi ini:DEPRECATION WARNING: Passing a template handler in the template name is deprecated. You can simply remove the handler name or pass render :handlers => [:erb] instead.
YWCA Halo
3
Satu kelemahan dari pendekatan ini adalah pelokalan yang akan dicari, foo.baz.html.[your_string]bukan foo.baz.[your_string]. Jawaban zgchurc adalah solusi yang lebih baik.
mbillard
32

Untuk Rails 3, blok with_format berfungsi, tetapi ini sedikit berbeda:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
sumber
Bekerja dengan rel 3.2.0 juga :)
gucki
32

Rails 4 akan memungkinkan Anda untuk melewatkan parameter format. Jadi Anda bisa melakukannya

render(:partial => 'form', :formats => [:html])} 

Perhatikan Anda dapat melakukan sesuatu yang serupa di Rails 3 tetapi itu tidak akan meneruskan format itu ke sub parsial mana pun (jika formulir memanggil parsial lain).

Anda dapat memiliki kemampuan Rails 4 di Rails 3 dengan membuat config / initializers / renderer.rb:

class ActionView::PartialRenderer
  private
  def setup_with_formats(context, options, block)
    formats = Array(options[:formats])
    @lookup_context.formats = formats | @lookup_context.formats
    setup_without_formats(context, options, block)
  end

  alias_method_chain :setup, :formats
end

Lihat http://railsguides.net/2012/08/29/rails3-does-not-render-p Partial-for-specific-format/

DrewB
sumber
Aneh, saya bertanya-tanya mengapa itu tidak berfungsi untuk saya di aplikasi Rails 3.2.19, bertanya-tanya apa yang membuat perbedaan di aplikasi saya. Tidak bisa mendapatkan render ActionView: parsial untuk menghormati: format arg, tampaknya tidak membuat perbedaan sama sekali, bahkan dengan tambalan ini.
jrochkind
29

Membangun tanggapan roninek , saya telah menemukan solusi terbaik sebagai berikut:

di /app/helpers/application.rb:

def with_format(format, &block)
  old_format = @template_format
  @template_format = format
  result = block.call
  @template_format = old_format
  return result
end

Di /app/views/foo/bar.json:

<% with_format('html') do %>
  <%= h render(:partial => '/foo/baz') %>
<% end %>

Solusi alternatifnya adalah dengan mendefinisikan kembali renderuntuk menerima a:format parameter.

Saya tidak bisa render :filebekerja dengan penduduk setempat dan tanpa jalan yang salah.

James A. Rosen
sumber
8
terlalu hack untuk selera saya. Lebih baik menentukan keseluruhan ekstensi.
Tim Harper
1
Untuk rel 3 lihat jawaban zgchurch.
lillq
25

Di Rails 3, Tampilan memiliki array format, yang berarti Anda dapat menyetelnya untuk mencari [: mobile,: html]. Setelan defaultnya adalah mencari: template seluler, tetapi kembali ke: template html. Efek pengaturan ini akan menurun menjadi bagian dalam.

Cara terbaik, tetapi masih cacat, yang dapat saya temukan untuk mengatur ini adalah dengan meletakkan baris ini di bagian atas setiap template seluler lengkap (tetapi tidak sebagian).

<% self.formats = [:mobile, :html] %>

Kekurangannya adalah Anda harus menambahkan baris itu ke beberapa templat. Jika ada yang tahu cara menyetel ini sekali, dari application_controller.rb, saya ingin mengetahuinya. Sayangnya, tidak berhasil menambahkan baris itu ke tata letak seluler Anda, karena templat dirender sebelum tata letak.

Tony Stubblebine
sumber
Terima kasih telah menjelaskan ini. Saya akhirnya menggunakannya di tempat lain: stackoverflow.com/questions/47459724/…
Joshua Pinter
16

Hanya menguraikan apa yang ditulis zgchurch:

  • dengan mempertimbangkan pengecualian
  • mengembalikan hasil dari blok yang dipanggil

Pikir itu mungkin berguna.

def with_format(format, &block)
  old_formats = formats
  begin
    self.formats = [format]
    return block.call
  ensure
    self.formats = old_formats
  end
end
viphe
sumber
10

Anda memiliki dua pilihan:

1) gunakan render :file

render :file => "foo/_baz.json.erb"

2) ubah format template menjadi html dengan menyetel variabel @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
roninek
sumber
1
<% @template_format = "html"%> bekerja untuk saya, karena sebagian saya memuat sebagian lainnya, saya tidak perlu memperbarui render lain di rantai.
pagetribe
5

Saya memiliki file bernama 'api / item.rabl' dan saya ingin merendernya dari tampilan HTML jadi saya harus menggunakan:

render file: 'api/item', formats: [:json]

( filekarena file tersebut tidak memiliki garis bawah pada namanya, formatsdan bukan format(serta pass dan array))

Dorian
sumber
2

Tampaknya mengoper formatsopsi akan membuatnya dengan benar di versi Rails yang lebih baru, setidaknya 3.2:

{
  someKey: 'some value',
  someHTML: "<%= h render('baz', formats: :html) -%>"
}
Mario Uher
sumber
1

Saya menemukan utas ini ketika saya mencoba membuat sebagian XML di file tampilan xml.builder lain. Berikut adalah cara yang bagus untuk melakukannya

xml.items :type => "array" do
    @items.each do |item|
        xml << render(:partial => 'shared/partial.xml.builder', :locals => { :item => item })
    end
end

Dan ya ... Nama file lengkap juga berfungsi di sini ...

Garfield
sumber
Ini menderita masalah bagian dalam yang sama dengan yang disebutkan @chrisrbailey pada jawaban lain: jika bagian yang Anda panggil dengan nama file lengkap itu sendiri menggunakan sebagian (tanpa menentukan nama file lengkap untuk masing-masing), itu akan gagal.
James A. Rosen