Wie rendere ich einen Teil eines anderen Formats in Rails?

114

Ich versuche, eine JSON-Antwort zu generieren, die HTML enthält. So habe ich /app/views/foo/bar.json.erb:

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

Ich möchte, dass es gerendert wird /app/views/foo/_baz.html.erb, aber es wird nur gerendert /app/views/foo/_baz.json.erb. Übergeben :format => 'html'hilft nicht.

James A. Rosen
quelle
Der Fragentitel ist viel allgemeiner als James 'spezielles Problem. Wenn Sie nach anderen Lösungen suchen, schauen Sie hier: stackoverflow.com/questions/7616097/…
miguelsan

Antworten:

100

Beginnend mit Rails 3.2.3 beim Aufrufen von render: teilweise (funktioniert nur außerhalb des respond_toBlocks).

render formats: [ :html ]

anstatt

render format: 'html'
Tim Haines
quelle
Ich denke, es gibt je nach Kontext unterschiedliche "beste" Antworten, daher ist es schwierig, diese Frage sinnvoll zu bewerten, aber dies erfüllte meine Notwendigkeit, eine HTML-Vorlage aus einem RJS mit demselben Namen einfach zu rendern, nachdem ich gehofft hatte, dass eine :formatOption dies tun würde Arbeit. Vielen Dank!
Ches
1
Dies funktioniert auch für Rails 5.0.2. Beachten Sie jedoch, dass Sie :content_typein einigen Fällen möglicherweise auch die Option angeben müssen - z. B. kann das Rendern einer HTML-Vorlage innerhalb eines format.pdfBlocks nur ausgeführt werden render "template", formats: [:html], content_type: "text/html". Ich brauche dies, weil ich das direkte Herunterladen von Mustern meiner Bücher nur für Mitglieder meiner E-Mail-Liste erlaube - regelmäßige Besucher erhalten stattdessen ein Anmeldeformular.
Michael Trojanek
64

Was ist falsch mit

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

? Ich habe gerade versucht, einen HTML-ERB-Teil aus einer Atom-Builder-Vorlage heraus zu rendern, und es hat gut funktioniert. Es ist kein Herumspielen mit globalen Variablen erforderlich (ja, ich weiß, dass sie "@" vor sich haben, aber genau das sind sie).

Ihr with_format &blockAnsatz ist jedoch cool und hat den Vorteil, dass Sie nur das Format angeben, während der einfache Ansatz auch die Vorlagen-Engine (ERB / ​​builder / etc) angibt.

Sam Stokes
quelle
26
Der einzige Nachteil dabei ist, dass wenn Ihr Teil andere Teilwerte rendert, dies fehlschlägt, es sei denn, Sie ändern alle Ihre Teilaufrufe zum Rendern, um die ".html.erb" in ihren Namen aufzunehmen.
Chrisrbailey
4
Sie müssen die Template-Engine nicht angeben, damit dies funktioniert. (Zumindest ab Schiene 3). Folgendes funktioniert einwandfrei: render (: teilweise => "baz.html")
Tim Harper
1
Funktioniert nicht, wenn Sie für verschiedene Gegenden unterschiedliche partials haben (zB baz.en.html.erb, baz.fr.html.erb) , und Sie wollen render :partials Logik für die richtige Wahl (mit Fallbacks, etc).
John
1
Ab Schiene 3.2.3 wird bei Verwendung dieser Lösung die folgende Warnung angezeigt: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 Hallo
3
Ein Nachteil dieses Ansatzes ist , dass Lokalisierungen für aussehen wird foo.baz.html.[your_string]statt foo.baz.[your_string]. Die Antwort von zgchurc ist eine bessere Lösung.
mbillard
32

Für Rails 3 funktioniert der with_format-Block, ist aber etwas anders:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
quelle
Funktioniert auch mit Rails 3.2.0 :)
Gucki
32

Mit Rails 4 können Sie einen Formatparameter übergeben. So können Sie tun

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

Beachten Sie, dass Sie in Rails 3 etwas Ähnliches tun können, dieses Format jedoch nicht an Subpartials übergeben wird (wenn das Formular andere Partials aufruft).

Sie können die Rails 4-Funktion in Rails 3 verwenden, indem Sie config / initializers / renderer.rb erstellen:

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

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

DrewB
quelle
Seltsamerweise frage ich mich, warum es in einer Rails 3.2.19-App nicht funktioniert. Ich frage mich, was den Unterschied in meiner App ausmacht. ActionView kann nicht gerendert werden: teilweise, um das Argument: formats zu respektieren, scheint selbst mit diesem Patch keinen Unterschied zu machen.
Jrochkind
29

Aufbauend auf Ronineks Antwort habe ich die beste Lösung gefunden:

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

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

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

Eine alternative Lösung wäre die Neudefinition render, um einen :formatParameter zu akzeptieren .

Ich konnte nicht render :filemit Einheimischen arbeiten und ohne ein bisschen Wonkiness.

James A. Rosen
quelle
8
zu hackisch für meinen Geschmack. Geben Sie besser die gesamte Erweiterung an.
Tim Harper
1
Für Schienen 3 siehe die Antwort von zgchurch.
lillq
25

In Rails 3 verfügt die Ansicht über ein Formatarray. Dies bedeutet, dass Sie festlegen können, dass nach [: mobile ,: html] gesucht wird. Bei dieser Einstellung wird standardmäßig nach: mobilen Vorlagen gesucht, aber auf: HTML-Vorlagen zurückgegriffen. Die Auswirkungen dieser Einstellung werden in innere Teilbereiche übergehen.

Der beste, aber immer noch fehlerhafte Weg, den ich finden konnte, um dies festzulegen, bestand darin, diese Zeile oben auf jeder vollständigen mobilen Vorlage (aber nicht auf Teilvorlagen) zu setzen.

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

Der Fehler ist, dass Sie diese Zeile mehreren Vorlagen hinzufügen müssen. Wenn jemand aus application_controller.rb eine Möglichkeit kennt, dies einmal festzulegen, würde ich es gerne wissen. Leider funktioniert es nicht, diese Zeile zu Ihrem mobilen Layout hinzuzufügen, da die Vorlagen vor dem Layout gerendert werden.

Tony Stubblebine
quelle
Vielen Dank für die Erklärung. Am
Joshua Pinter
16

Ich erläutere nur, was zgchurch geschrieben hat:

  • Ausnahmen berücksichtigen
  • Rückgabe des Ergebnisses des aufgerufenen Blocks

Ich dachte, es könnte nützlich sein.

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

Sie haben zwei Möglichkeiten:

1) verwenden render :file

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

2) Ändern Sie das Vorlagenformat in HTML, indem Sie die Variable @template_format festlegen

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
Roninek
quelle
1
<% @template_format = "html"%> funktionierte für mich, da meine teilweise geladenen anderen Partials die anderen Renderings in der Kette nicht aktualisieren mussten.
Pagetribe
5

Ich hatte eine Datei mit dem Namen 'api / item.rabl' und wollte sie aus einer HTML-Ansicht rendern, also musste ich Folgendes verwenden:

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

( fileweil die Datei keinen Unterstrich im Namen hat formatsund nicht format(und Pässe und Array))

Dorian
quelle
2

Es scheint, dass das Übergeben einer formatsOption diese in der neueren Rails-Version, mindestens 3.2, ordnungsgemäß wiedergibt:

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

Ich bin auf diesen Thread gestoßen, als ich versucht habe, einen XML-Teil in einer anderen xml.builder-Ansichtsdatei zu rendern. Das Folgende ist ein guter Weg, um es zu tun

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

Und ja ... Der vollständige Dateiname funktioniert auch hier ...

Garfield
quelle
Dies leidet unter dem gleichen Inner-Partials-Problem, das @chrisrbailey in einer anderen Antwort erwähnt hat: Wenn der Teil, den Sie mit einem vollständigen Dateinamen aufrufen, Partials verwendet (ohne den vollständigen Dateinamen für jeden anzugeben), schlägt dies fehl.
James A. Rosen