Comment rendre un partiel d'un format différent dans Rails?

114

J'essaye de générer une réponse JSON qui inclut du HTML. Ainsi, j'ai /app/views/foo/bar.json.erb:

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

Je veux qu'il soit rendu /app/views/foo/_baz.html.erb, mais il ne fera que rendre /app/views/foo/_baz.json.erb. Passer :format => 'html'n'aide pas.

James A. Rosen
la source
Le titre de la question est beaucoup plus général que le problème particulier de James. Si vous cherchez d'autres solutions, jetez un œil ici: stackoverflow.com/questions/7616097/…
miguelsan

Réponses:

100

À partir de Rails 3.2.3, lors de l'appel de render: partial (ne fonctionne qu'en dehors du respond_tobloc).

render formats: [ :html ]

au lieu de

render format: 'html'
Tim Haines
la source
Je suppose qu'il peut y avoir différentes «meilleures» réponses en fonction du contexte, il est donc difficile de voter positivement sur cette question, mais cela a comblé mon besoin de rendre facilement un modèle HTML à partir d'un RJS du même nom après avoir espéré qu'une :formatoption le ferait travail. Merci!
ches
1
Cela fonctionne également pour Rails 5.0.2. Cependant, notez que vous devrez peut-être également spécifier l' :content_typeoption dans certains cas - par exemple, le rendu d'un modèle HTML à partir d'un format.pdfbloc ne peut être effectué qu'avec render "template", formats: [:html], content_type: "text/html". J'en ai besoin car j'autorise le téléchargement direct d'échantillons de mes livres uniquement pour les membres de ma liste de diffusion - les visiteurs réguliers reçoivent un formulaire d'inscription à la place.
Michael Trojanek
64

Quel est le problème avec

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

? J'ai juste essayé ceci pour rendre un HTML ERB partiel à partir d'un modèle de générateur Atom et cela a bien fonctionné. Pas de dérangement avec les variables globales requises (ouais, je sais qu'ils ont "@" devant eux, mais c'est ce qu'ils sont).

Votre with_format &blockapproche est cool cependant, et a l'avantage que vous ne spécifiez que le format, tandis que l'approche simple spécifie également le moteur de modèle (ERB / ​​builder / etc).

Sam Stokes
la source
26
Le seul inconvénient est que si votre partiel rend d'autres partiels, il échouera à moins que vous n'interveniez et que vous changiez tous vos appels partiels de rendu pour inclure le ".html.erb" sur leur nom.
chrisrbailey
4
vous n'avez pas besoin de spécifier le moteur de création de modèles pour que cela fonctionne. (Au moins à partir des rails 3). Ce qui suit fonctionne très bien: render (: partial => "baz.html")
Tim Harper
1
Ne fonctionne pas si vous avez des partiels différents pour différentes locales (par exemple baz.en.html.erb, baz.fr.html.erb) et que vous voulez render :partialune logique pour choisir le bon (avec des solutions de secours, etc.).
John
1
À partir des rails 3.2.3, je vois l'avertissement suivant lors de l'utilisation de cette solution: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 Bonjour
3
Un inconvénient de cette approche est que les localisations rechercheront au foo.baz.html.[your_string]lieu de foo.baz.[your_string]. La réponse de zgchurc est une meilleure solution.
mbillard
32

Pour Rails 3, le bloc with_format fonctionne, mais c'est un peu différent:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
la source
Fonctionne aussi avec les rails 3.2.0 :)
gucki
32

Rails 4 vous permettra de passer un paramètre de formats. Alors tu peux faire

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

Notez que vous pouvez faire quelque chose de similaire dans Rails 3, mais cela ne transmettra pas ce format à aucun sous-partiel (si form appelle d'autres partiels).

Vous pouvez avoir la capacité Rails 4 dans Rails 3 en créant 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

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

DrewB
la source
étrange, je me demande pourquoi cela ne fonctionne pas pour moi dans une application Rails 3.2.19, je me demande ce qui fait la différence dans mon application. Impossible d'obtenir le rendu ActionView: partial pour respecter l'argument: formats, semble ne faire aucune différence, même avec ce patch.
jrochkind
29

En m'appuyant sur la réponse de roninek , j'ai trouvé la meilleure solution:

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

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

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

Une autre solution serait de redéfinir renderpour accepter un :formatparamètre.

Je ne pouvais pas render :filetravailler avec les locaux et sans une certaine imprudence.

James A. Rosen
la source
8
trop hackish à mes goûts. Mieux vaut spécifier l'extension entière.
Tim Harper
1
Pour les rails 3, voir la réponse de zgchurch.
lillq
25

Dans Rails 3, la vue a un tableau de formats, ce qui signifie que vous pouvez la définir pour rechercher [: mobile,: html]. Paramètre qui recherchera par défaut: les modèles mobiles, mais reviendra à: les modèles html. Les effets de ce réglage se répercuteront sur les partiels internes.

La meilleure façon, mais toujours imparfaite, que j'ai pu trouver pour définir cela était de mettre cette ligne en haut de chaque modèle mobile complet (mais pas de partiels).

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

Le défaut est que vous devez ajouter cette ligne à plusieurs modèles. Si quelqu'un connaît un moyen de définir cela une fois, à partir de application_controller.rb, j'aimerais le savoir. Malheureusement, cela ne fonctionne pas pour ajouter cette ligne à votre mise en page mobile, car les modèles sont rendus avant la mise en page.

Tony Stubblebine
la source
Merci d'avoir expliqué cela. J'ai fini par l'utiliser ailleurs: stackoverflow.com/questions/47459724/…
Joshua Pinter
16

En expliquant simplement ce que zgchurch a écrit:

  • en tenant compte des exceptions
  • renvoyer le résultat du bloc appelé

J'ai pensé que cela pourrait être utile.

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

Vous avez deux options:

1) utiliser render :file

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

2) changer le format du modèle en html en définissant la variable @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
Roninek
la source
1
<% @template_format = "html"%> a fonctionné pour moi, car mon partiel chargé d'autres partiels je n'ai pas eu à mettre à jour les autres rendus dans la chaîne.
pagetribe
5

J'avais un fichier nommé 'api / item.rabl' et je voulais le rendre à partir d'une vue HTML, j'ai donc dû utiliser:

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

( fileparce que le fichier n'a pas de trait de soulignement dans le nom, formatset non format(et passe et tableau))

Dorian
la source
2

Il semble que passer une formatsoption la rendra correctement dans la nouvelle version de Rails, au moins 3.2:

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

Je suis tombé sur ce fil lorsque j'essayais de rendre un partiel XML dans un autre fichier de vue xml.builder. Suivre est une bonne façon de le faire

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

Et ouais ... Le nom complet du fichier fonctionne ici aussi ...

Garfield
la source
Cela souffre du même problème partiel interne que @chrisrbailey a mentionné dans une autre réponse: si le partiel que vous appelez avec un nom de fichier complet utilise lui-même des partiels (sans spécifier le nom complet du fichier pour chacun), il échouera.
James A. Rosen