Come posso eseguire il rendering di una parte di un formato diverso in Rails?


114

Sto cercando di generare una risposta JSON che includa del codice HTML. Quindi, ho /app/views/foo/bar.json.erb:

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

Voglio che venga eseguito il rendering /app/views/foo/_baz.html.erb, ma eseguirà solo il rendering /app/views/foo/_baz.json.erb. Passare :format => 'html'non aiuta.


Il titolo della domanda è molto più generale del problema particolare di James. Se siete alla ricerca di altre soluzioni, date un'occhiata qui: stackoverflow.com/questions/7616097/...
miguelsan

Risposte:


100

A partire da Rails 3.2.3, quando si chiama render: partial (funziona solo al di fuori del respond_toblocco).

render formats: [ :html ]

invece di

render format: 'html'

Immagino che potrebbero esserci diverse risposte "migliori" a seconda del contesto, quindi è difficile esprimere un voto positivo su questa domanda, ma questo ha soddisfatto la mia esigenza di rendere facilmente un modello HTML dall'interno di un RJS con lo stesso nome dopo aver sperato che :formatun'opzione fosse lavoro. Grazie!
ches

1
Questo funziona anche per Rails 5.0.2. Tuttavia, tieni presente che potresti anche dover specificare l' :content_typeopzione in alcuni casi, ad esempio il rendering di un modello HTML dall'interno di un format.pdfblocco può essere fatto solo con render "template", formats: [:html], content_type: "text/html". Ne ho bisogno perché consento il download diretto di campioni dei miei libri solo per i membri della mia lista di posta elettronica: i visitatori regolari ottengono invece un modulo di registrazione.
Michael Trojanek

64

Cosa c'è che non va

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

? Ho appena provato questo per eseguire il rendering di un parziale ERB HTML dall'interno di un modello di builder Atom e ha funzionato bene. Non è necessario scherzare con le variabili globali (sì, lo so che hanno "@" davanti, ma è quello che sono).

Il tuo with_format &blockapproccio è comunque interessante e ha il vantaggio di specificare solo il formato, mentre l'approccio semplice specifica anche il motore del modello (ERB / ​​builder / ecc.).


26
L'unico svantaggio di questo è che se il tuo partial esegue il rendering di altri partial, fallirà a meno che tu non entri e modifichi tutte le tue chiamate di render partial per includere ".html.erb" nel loro nome.
chrisrbailey

4
non è necessario specificare il motore di template affinché funzioni. (Almeno a partire dai binari 3). Il seguente funziona perfettamente: render (: partial => "baz.html")
Tim Harper

1
Non funziona se hai parziali differenti per differenti localizzazioni (es baz.en.html.erb. baz.fr.html.erb) E vuoi render :partialla logica per scegliere quella giusta (con fallback, ecc.).
John

1
A partire da rails 3.2.3, vedo il seguente avviso quando utilizzo questa soluzione: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
Uno svantaggio di questo approccio è che le localizzazioni cercheranno foo.baz.html.[your_string]invece di foo.baz.[your_string]. La risposta di zgchurc è una soluzione migliore.
mbillard

32

Per Rails 3, il blocco with_format funziona, ma è leggermente diverso:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end

Funziona anche con i binari 3.2.0 :)
gucki

32

Rails 4 ti permetterà di passare un parametro di formattazione. Quindi puoi farlo

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

Nota che puoi fare qualcosa di simile in Rails 3 ma non passerà quel formato a nessun sub partials (se il form chiama altri partials).

Puoi avere l'abilità di Rails 4 in Rails 3 creando 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

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


strano, mi chiedo perché non funziona per me in un'app Rails 3.2.19, mi chiedo cosa faccia la differenza nella mia app. Non è possibile ottenere il rendering di ActionView: partial per rispettare i formati arg, sembra non fare alcuna differenza, anche con questa patch.
jrochkind

29

Sulla risposta di roninek , ho trovato la soluzione migliore per essere il seguente:

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

Una soluzione alternativa sarebbe ridefinire renderper accettare un :formatparametro.

Non riuscivo render :filea lavorare con la gente del posto e senza qualche perplessità sul percorso.


8
troppo hacker per i miei gusti. Meglio specificare l'intera estensione.
Tim Harper

1
Per i binari 3 vedere la risposta di zgchurch.
lillq

25

In Rails 3, la vista ha un array di formati, il che significa che puoi impostarlo per cercare [: mobile,: html]. L'impostazione predefinita prevede la ricerca di: modelli per dispositivi mobili, ma ricade su: modelli html. Gli effetti dell'impostazione di questo si estenderanno alle parziali interne.

Il modo migliore, ma ancora imperfetto, che ho trovato per impostare questo è stato quello di mettere questa linea nella parte superiore di ogni modello mobile completo (ma non parziale).

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

Il difetto è che devi aggiungere quella riga a più modelli. Se qualcuno conosce un modo per impostarlo una volta, da application_controller.rb, mi piacerebbe saperlo. Sfortunatamente, non funziona aggiungere quella linea al layout mobile, perché i modelli vengono renderizzati prima del layout.


Grazie per aver spiegato questo. Ho finito per usarlo altrove: stackoverflow.com/questions/47459724/…
Joshua Pinter

16

Sto solo elaborando ciò che ha scritto zgchurch:

  • tenendo conto delle eccezioni
  • restituendo il risultato del blocco chiamato

Ho pensato che potrebbe essere utile.

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

10

Hai due opzioni:

1) utilizzo render :file

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

2) cambia il formato del modello in html impostando la variabile @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>

1
<% @template_format = "html"%> ha funzionato per me, poiché i miei altri parziali a caricamento parziale non dovevo aggiornare gli altri rendering lungo la catena.
pagetribe

5

Avevo un file chiamato 'api / item.rabl' e volevo renderlo da una visualizzazione HTML, quindi ho dovuto usare:

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

( fileperché il file non ha un carattere di sottolineatura nel nome formatse non format(e passa e matrice))


2

Sembra che passare formatsun'opzione la renderà correttamente nella versione più recente di Rails, almeno la 3.2:

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

1

Mi sono imbattuto in questo thread quando stavo cercando di eseguire il rendering di un parziale XML in un altro file di visualizzazione xml.builder. Di seguito è un bel modo per farlo

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

E sì ... il nome completo del file funziona anche qui ...


Questo soffre dello stesso problema di inner-partials menzionato da @chrisrbailey in un'altra risposta: se il partial che chiami con un nome di file completo utilizza i partials (senza specificare il nome di file completo per ciascuno), fallirà.
James A. Rosen
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.