¿Cómo renderizo un parcial de un formato diferente en Rails?

114

Estoy intentando generar una respuesta JSON que incluya algo de HTML. Por lo tanto, tengo /app/views/foo/bar.json.erb:

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

Quiero que se renderice /app/views/foo/_baz.html.erb, pero solo se renderizará /app/views/foo/_baz.json.erb. Pasar :format => 'html'no ayuda.

James A. Rosen
fuente
El título de la pregunta es mucho más general que el problema particular de James. Si está buscando otras soluciones, eche un vistazo aquí: stackoverflow.com/questions/7616097/…
miguelsan

Respuestas:

100

Comenzando con Rails 3.2.3, al llamar a render: parcial (solo funciona fuera del respond_tobloque).

render formats: [ :html ]

en vez de

render format: 'html'
Tim Haines
fuente
Supongo que puede haber diferentes "mejores" respuestas según el contexto, por lo que es difícil votar positivamente en esta pregunta, pero esto satisfizo mi necesidad de representar fácilmente una plantilla HTML desde un RJS con el mismo nombre después de esperar que una :formatopción lo hiciera trabajo. ¡Gracias!
ches
1
Esto también funciona para Rails 5.0.2. Sin embargo, tenga en cuenta que es posible que también deba especificar la :content_typeopción en algunos casos; por ejemplo, format.pdfsolo se puede hacer con renderizar una plantilla HTML desde el interior de un bloque render "template", formats: [:html], content_type: "text/html". Lo necesito porque permito la descarga directa de muestras de mis libros solo para los miembros de mi lista de correo electrónico; los visitantes habituales obtienen un formulario de registro en su lugar.
Michael Trojanek
64

Qué hay de malo en

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

? Intenté esto para renderizar un HTML ERB parcial desde dentro de una plantilla de generador Atom y funcionó bien. No es necesario jugar con las variables globales (sí, sé que tienen "@" delante de ellas, pero eso es lo que son).

Sin embargo, su with_format &blockenfoque es genial y tiene la ventaja de que solo especifica el formato, mientras que el enfoque simple especifica el motor de plantilla (ERB / ​​builder / etc.) también.

Sam Stokes
fuente
26
El único inconveniente de esto es que si su parcial renderiza otros parciales, fallará a menos que entre y cambie todas las llamadas parciales de renderizado para incluir ".html.erb" en su nombre.
chrisrbailey
4
no es necesario especificar el motor de plantillas para que esto funcione. (Al menos a partir de los carriles 3). Lo siguiente funciona bien: render (: partial => "baz.html")
Tim Harper
1
No funciona si tiene diferentes parciales para diferentes configuraciones regionales (por ejemplo baz.en.html.erb, baz.fr.html.erb) y desea render :partialuna lógica para elegir la correcta (con alternativas, etc.).
Juan
1
A partir de rieles 3.2.3, veo la siguiente advertencia al usar esta solución: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 Hola
3
Una desventaja de este enfoque es que las localizaciones buscarán en foo.baz.html.[your_string]lugar de foo.baz.[your_string]. La respuesta de zgchurc es una mejor solución.
mbillard
32

Para Rails 3, el bloque with_format funciona, pero es un poco diferente:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
fuente
También funciona con rieles 3.2.0 :)
gucki
32

Rails 4 te permitirá pasar un parámetro de formatos. Para que puedas hacer

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

Tenga en cuenta que puede hacer algo similar en Rails 3, pero no pasaría ese formato a ningún subparcial (si el formulario llama a otros parciales).

Puede tener la capacidad de Rails 4 en 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

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

DrewB
fuente
curioso, me pregunto por qué no me funciona en una aplicación Rails 3.2.19, me pregunto qué marca la diferencia en mi aplicación. No se puede obtener el renderizado de ActionView: parcial para respetar el: formatos arg, parece no hacer ninguna diferencia, incluso con este parche.
jrochkind
29

Sobre la base de la respuesta de roninek , he encontrado que la mejor solución es la siguiente:

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

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

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

Una solución alternativa sería redefinir renderpara aceptar un :formatparámetro.

No pude llegar render :filea trabajar con los lugareños y sin algunas dificultades en el camino.

James A. Rosen
fuente
8
demasiado hackish para mi gusto. Es mejor especificar la extensión completa.
Tim Harper
1
Para los rieles 3, consulte la respuesta de zgchurch.
lillq
25

En Rails 3, la Vista tiene una matriz de formatos, lo que significa que puede configurarla para buscar [: mobile,: html]. Configuración predeterminada para buscar: plantillas móviles, pero recurrir a: plantillas html. Los efectos de configurar esto caerán en cascada en parciales internos.

La mejor manera, pero aún defectuosa, que pude encontrar para configurar esto fue poner esta línea en la parte superior de cada plantilla móvil completa (pero no parciales).

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

El defecto es que tienes que agregar esa línea a varias plantillas. Si alguien conoce una forma de configurar esto una vez, desde application_controller.rb, me encantaría saberlo. Desafortunadamente, no funciona agregar esa línea a su diseño móvil, porque las plantillas se representan antes que el diseño.

Tony Stubblebine
fuente
Gracias por explicar esto. Terminé usándolo en otro lugar: stackoverflow.com/questions/47459724/…
Joshua Pinter
16

Simplemente explicando lo que escribió zgchurch:

  • teniendo en cuenta las excepciones
  • devolviendo el resultado del bloque llamado

Pensé que podría ser útil.

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

Tienes dos opciones:

1) uso render :file

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

2) cambie el formato de la plantilla a html configurando la variable @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
roninek
fuente
1
<% @template_format = "html"%> funcionó para mí, ya que mis otros parciales cargados parciales no tuve que actualizar los otros renders en la cadena.
pagetribe
5

Tenía un archivo llamado 'api / item.rabl' y quería renderizarlo desde una vista HTML, así que tuve que usar:

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

( fileporque el archivo no tiene guión bajo en el nombre, formatsy no format(y pasa y matriz))

dorio
fuente
2

Parece que pasar una formatsopción la renderizará correctamente en la versión más reciente de Rails, al menos 3.2:

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

Me encontré con este hilo cuando intentaba renderizar un XML parcial en otro archivo de vista xml.builder. Lo siguiente es una buena manera de hacerlo.

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

Y sí ... El nombre de archivo completo también funciona aquí ...

Garfield
fuente
Esto sufre el mismo problema de parciales internos que @chrisrbailey mencionó en otra respuesta: si el parcial al que llama con un nombre de archivo completo usa parciales (sin especificar el nombre de archivo completo para cada uno), fallará.
James A. Rosen