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.


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'

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.


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

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/


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.


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.


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

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

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


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) -%>"
}

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.