Jak wyrenderować część innego formatu w Railsach?

114

Próbuję wygenerować odpowiedź JSON, która zawiera kod HTML. Tak więc mam /app/views/foo/bar.json.erb:

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

Chcę, aby renderował /app/views/foo/_baz.html.erb, ale będzie to renderować tylko /app/views/foo/_baz.json.erb. Podawanie :format => 'html'nie pomaga.

James A. Rosen
źródło
Tytuł pytania jest dużo bardziej ogólny niż konkretny problem Jamesa. Jeśli szukasz innych rozwiązań, zajrzyj tutaj: stackoverflow.com/questions/7616097/…
miguelsan

Odpowiedzi:

100

Począwszy od Railsów 3.2.3, podczas wywoływania render: parts (działa tylko poza respond_toblokiem).

render formats: [ :html ]

zamiast

render format: 'html'
Tim Haines
źródło
Wydaje mi się, że w zależności od kontekstu mogą istnieć różne „najlepsze” odpowiedzi, więc trudno jest mi w znaczący sposób zagłosować za tym pytaniem, ale to wypełniło moją potrzebę łatwego renderowania szablonu HTML z poziomu RJS o tej samej nazwie, mając nadzieję, że :formatopcja praca. Dzięki!
ches
1
Działa to również w Railsach 5.0.2. Należy jednak pamiętać, że :content_typew niektórych przypadkach może być konieczne określenie tej opcji - np. Renderowanie szablonu HTML z wnętrza format.pdfbloku można wykonać tylko za pomocą render "template", formats: [:html], content_type: "text/html". Potrzebuję tego, ponieważ zezwalam na bezpośrednie pobieranie próbek moich książek tylko dla członków mojej listy e-mailowej - zwykli goście otrzymują zamiast tego formularz rejestracyjny.
Michael Trojanek
64

co jest źle z

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

? Właśnie próbowałem tego wyrenderować część HTML ERB z wnętrza szablonu Atom builder i działało dobrze. Nie trzeba bawić się zmiennymi globalnymi (tak, wiem, że mają przed sobą znak „@”, ale tym właśnie są).

Twoje with_format &blockpodejście jest jednak fajne i ma tę zaletę, że określasz tylko format, podczas gdy proste podejście określa również silnik szablonu (ERB / ​​builder / etc).

Sam Stokes
źródło
26
Jedynym minusem jest to, że jeśli twoje częściowe renderowanie innych podszablonów zakończy się niepowodzeniem, chyba że wejdziesz i zmienisz wszystkie wywołania częściowe renderowania tak, aby zawierały „.html.erb” w ich nazwie.
chrisrbailey,
4
nie musisz określać silnika szablonów, aby to działało. (Przynajmniej od szyn 3). Następujące polecenie działa dobrze: render (: parts => "baz.html")
Tim Harper
1
Nie działa, jeśli mają różne partials w różnych lokalizacjach (np baz.en.html.erb, baz.fr.html.erb) i chcesz render :partials logikę wyborze jednego (z fallbacks, etc).
John
1
Jeśli chodzi o szyny 3.2.3, podczas korzystania z tego rozwiązania widzę następujące ostrzeżenie: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 Hello
3
Wadą tego podejścia jest to, że lokalizacje będą szukać foo.baz.html.[your_string]zamiast foo.baz.[your_string]. odpowiedzią zgchurc jest lepsze rozwiązanie.
mbillard
32

W Railsach 3 blok with_format działa, ale jest trochę inny:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
źródło
Działa też z szynami 3.2.0 :)
gucki
32

Rails 4 pozwolą ci na przekazanie parametru formatów. Więc możesz to zrobić

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

Zauważ, że możesz zrobić coś podobnego w Railsach 3, ale nie przekaże tego formatu do żadnych części podrzędnych (jeśli formularz wywoła inne części podrzędne).

Możesz mieć możliwość Rails 4 w Railsach 3, tworząc 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

Zobacz http://railsguides.net/2012/08/29/rails3-does-not-render-partial-for-specific-format/

DrewB
źródło
dziwne, zastanawiam się, dlaczego to nie działa dla mnie w aplikacji Rails 3.2.19, zastanawiam się, jaka jest różnica w mojej aplikacji. Nie można uzyskać renderowania ActionView: częściowe poszanowanie argumentu: formaty, wydaje się nie robić żadnej różnicy, nawet z tą poprawką.
jrochkind,
29

Opierając się na odpowiedzi Roninka , znalazłem najlepsze rozwiązanie:

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

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

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

Alternatywnym rozwiązaniem byłaby zmiana definicji w rendercelu zaakceptowania :formatparametru.

Nie mogłem render :filepracować z lokalnymi mieszkańcami i bez pewnej chwiejnej ścieżki.

James A. Rosen
źródło
8
zbyt hacky jak na mój gust. Lepiej określić całe rozszerzenie.
Tim Harper
1
Dla szyn 3 patrz odpowiedź zgchurch.
lillq
25

W Railsach 3 widok ma tablicę formatów, co oznacza, że ​​możesz ustawić, aby szukał [: mobile,: html]. Ustawienie to będzie domyślnie szukać: szablonów mobilnych, ale wróci do: szablonów html. Skutki ustawienia tego ustawienia spadną kaskadowo do wewnętrznych części składowych.

Najlepszym, ale wciąż wadliwym sposobem, jaki mogłem znaleźć, aby to ustawić, było umieszczenie tej linii na górze każdego pełnego szablonu mobilnego (ale nie częściowego).

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

Wada polega na tym, że musisz dodać tę linię do wielu szablonów. Jeśli ktoś zna sposób, aby to ustawić raz, z application_controller.rb, chciałbym to wiedzieć. Niestety dodanie tej linii do układu mobilnego nie działa, ponieważ szablony są renderowane przed układem.

Tony Stubblebine
źródło
Dziękuję za wyjaśnienie. Skończyło się na tym, że użyłem go gdzie indziej: stackoverflow.com/questions/47459724/ ...
Joshua Pinter,
16

Rozwijając tylko to, co napisał zgchurch:

  • biorąc pod uwagę wyjątki
  • zwracanie wyniku wywołanego bloku

Pomyślałem, że to może być przydatne.

def with_format(format, &block)
  old_formats = formats
  begin
    self.formats = [format]
    return block.call
  ensure
    self.formats = old_formats
  end
end
viphe
źródło
10

Masz dwie możliwości:

1) użycie render :file

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

2) zmień format szablonu na html, ustawiając zmienną @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
Roninek
źródło
1
<% @template_format = "html"%> działało dla mnie, ponieważ moje częściowo załadowane inne części podrzędne nie musiałem aktualizować innych renderów w dół łańcucha.
pagetribe
5

Miałem plik o nazwie „api / item.rabl” i chciałem go wyrenderować z widoku HTML, więc musiałem użyć:

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

( fileponieważ plik nie ma podkreślenia w nazwie, formatsa nie format(i przebiegach i tablicy))

dorycki
źródło
2

Wygląda na to, że przekazanie formatsopcji wyrenderuje ją poprawnie w nowszej wersji Railsów, co najmniej 3.2:

{
  someKey: 'some value',
  someHTML: "<%= h render('baz', formats: :html) -%>"
}
Mario Uher
źródło
1

Natknąłem się na ten wątek, gdy próbowałem renderować część XML w innym pliku widoku xml.builder. Podążanie to dobry sposób na zrobienie tego

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

I tak ... Pełna nazwa pliku też działa tutaj ...

Garfield
źródło
Cierpi na ten sam problem z wewnętrznymi częściami częściowymi, o którym @chrisrbailey wspomniał w innej odpowiedzi: jeśli wywoływana część z pełną nazwą pliku sama używa częściowych (bez określania pełnej nazwy pliku dla każdego), zakończy się niepowodzeniem.
James A. Rosen