Como faço para renderizar uma parcial de um formato diferente no Rails?

114

Estou tentando gerar uma resposta JSON que inclui algum HTML. Assim, tenho /app/views/foo/bar.json.erb:

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

Eu quero renderizar /app/views/foo/_baz.html.erb, mas só renderá /app/views/foo/_baz.json.erb. Passar :format => 'html'não ajuda.

James A. Rosen
fonte
O título da pergunta é muito mais geral do que o problema particular de James. Se você estiver procurando por outras soluções, dê uma olhada aqui: stackoverflow.com/questions/7616097/…
miguelsan

Respostas:

100

Começando com Rails 3.2.3, ao chamar render: partial (só funciona fora do respond_tobloco).

render formats: [ :html ]

ao invés de

render format: 'html'
Tim Haines
fonte
Eu acho que pode haver diferentes "melhores" respostas dependendo do contexto, então é difícil votar positivamente nesta questão, mas isso preencheu minha necessidade de renderizar facilmente um modelo HTML de dentro de um RJS com o mesmo nome depois de esperar que uma :formatopção trabalhos. Obrigado!
ches
1
Isso também funciona para Rails 5.0.2. No entanto, observe que você também pode precisar especificar a :content_typeopção em alguns casos - por exemplo, renderizar um modelo HTML de dentro de um format.pdfbloco só pode ser feito com render "template", formats: [:html], content_type: "text/html". Preciso disso porque permito o download direto de amostras dos meus livros apenas para membros da minha lista de e-mail - visitantes regulares recebem um formulário de inscrição.
Michael Trojanek
64

O que há de errado com

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

? Eu apenas tentei isso para renderizar um HTML ERB parcial de dentro de um modelo de construtor Atom e funcionou bem. Não é necessário brincar com variáveis ​​globais (sim, eu sei que eles têm "@" na frente deles, mas é o que são).

Sua with_format &blockabordagem é legal, porém, e tem a vantagem de especificar apenas o formato, enquanto a abordagem simples especifica o mecanismo de modelo (ERB / ​​builder / etc) também.

Sam Stokes
fonte
26
A única desvantagem disso é que se o seu parcial renderizar outros parciais, ele falhará a menos que você vá e mude todas as suas chamadas parciais de renderização para incluir ".html.erb" em seus nomes.
chrisrbailey
4
você não precisa especificar o mecanismo de modelagem para que isso funcione. (Pelo menos a partir dos trilhos 3). O seguinte funciona perfeitamente: render (: partial => "baz.html")
Tim Harper
1
Não funciona se você tiver parciais diferentes para localidades diferentes (por exemplo baz.en.html.erb, baz.fr.html.erb) e desejar render :partialuma lógica para escolher a correta (com fallbacks, etc).
João
1
No trilho 3.2.3, estou vendo o seguinte aviso ao usar essa solução: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 Olá
3
Uma desvantagem dessa abordagem é que as localizações procurarão em foo.baz.html.[your_string]vez de foo.baz.[your_string]. A resposta de zgchurc é uma solução melhor.
mbillard
32

Para Rails 3, o bloco with_format funciona, mas é um pouco diferente:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
fonte
Também funciona com rails 3.2.0 :)
gucki
32

O Rails 4 permitirá que você passe um parâmetro de formatos. Então você pode fazer

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

Note que você pode fazer algo semelhante no Rails 3, mas não passaria esse formato para nenhuma subparcial (se o formulário chamar outras parciais).

Você pode ter a capacidade do Rails 4 no Rails 3 criando 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

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

DrewB
fonte
estranho, eu me pergunto por que não está funcionando para mim em um aplicativo Rails 3.2.19, me pergunto o que faz a diferença no meu aplicativo. Não é possível obter ActionView render: parcial para respeitar o: formatações arg, parece não fazer nenhuma diferença, mesmo com este patch.
jrochkind,
29

Com base na resposta de Roninek , descobri que a melhor solução é a seguinte:

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

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

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

Uma solução alternativa seria redefinir renderpara aceitar um :formatparâmetro.

Eu não conseguia render :filetrabalhar com os moradores locais e sem alguns problemas de caminho.

James A. Rosen
fonte
8
muito hackish para o meu gosto. Melhor especificar toda a extensão.
Tim Harper
1
Para os trilhos 3, consulte a resposta de zgchurch.
lillq
25

No Rails 3, a View possui um array de formatos, o que significa que você pode configurá-la para procurar [: mobile,: html]. A configuração padrão é procurar: modelos móveis, mas voltar para: modelos html. Os efeitos dessa configuração irão se espalhar para os parciais internos.

A melhor, mas ainda assim falha, que encontrei para definir isso foi colocar essa linha no topo de cada modelo completo para celular (mas não parciais).

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

A falha é que você precisa adicionar essa linha a vários modelos. Se alguém souber uma maneira de definir isso uma vez, em application_controller.rb, adoraria saber. Infelizmente, não adianta adicionar essa linha ao layout móvel, porque os modelos são renderizados antes do layout.

Tony Stubblebine
fonte
Obrigado por explicar isso. Acabei usando em outro lugar: stackoverflow.com/questions/47459724/…
Joshua Pinter
16

Apenas elaborando o que zgchurch escreveu:

  • levando as exceções em consideração
  • retornando o resultado do bloco chamado

Achei que poderia 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
fonte
10

Você tem duas opções:

1) usar render :file

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

2) alterar o formato do modelo para html definindo a variável @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
Roninek
fonte
1
<% @template_format = "html"%> funcionou para mim, como meu parcial carregou outros parciais, não tive que atualizar os outros renderizadores na cadeia.
pagetribe
5

Eu tinha um arquivo chamado 'api / item.rabl' e queria renderizá-lo a partir de uma visualização HTML, então tive que usar:

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

( fileporque o arquivo não tem sublinhado no nome, formatse não format(e passa e matriz))

Dorian
fonte
2

Parece que passar uma formatsopção irá renderizá-la corretamente na versão mais recente do Rails, pelo menos 3.2:

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

Eu me deparei com esse tópico quando estava tentando renderizar um XML parcial em outro arquivo de exibição xml.builder. Seguir é uma boa maneira de fazer isso

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

E sim ... O nome completo do arquivo também funciona aqui ...

Garfield
fonte
Isso sofre do mesmo problema de parciais internos que @chrisrbailey mencionou em outra resposta: se o parcial que você chama com um nome de arquivo completo usa parciais (sem especificar o nome completo do arquivo para cada um), ele falhará.
James A. Rosen