Как мне отрендерить партиал другого формата в Rails?

115

Я пытаюсь создать ответ JSON, содержащий некоторый HTML. Таким образом, у меня есть /app/views/foo/bar.json.erb:

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

Я хочу его рендерить /app/views/foo/_baz.html.erb, но он будет только рендеринг /app/views/foo/_baz.json.erb. Прохождение :format => 'html'не помогает.

Джеймс А. Розен
источник
Заголовок вопроса гораздо более общий, чем конкретная проблема Джеймса. Если вы ищете другие решения, загляните сюда: stackoverflow.com/questions/7616097/…
miguelsan

Ответы:

101

Начиная с Rails 3.2.3, при вызове render: partial (работает только вне respond_toблока).

render formats: [ :html ]

вместо того

render format: 'html'
Тим Хейнс
источник
Я предполагаю, что в зависимости от контекста могут быть разные «лучшие» ответы, поэтому сложно осмысленно проголосовать за этот вопрос, но это удовлетворило мою потребность в простом рендеринге HTML-шаблона из RJS с одним и тем же именем после надежды, что :formatвариант будет работай. Спасибо!
ches
1
Это также работает для Rails 5.0.2. Однако обратите внимание, что вам также может потребоваться указать этот :content_typeпараметр в некоторых случаях - например, рендеринг HTML-шаблона изнутри format.pdfблока может быть выполнен только с render "template", formats: [:html], content_type: "text/html". Мне это нужно, потому что я разрешаю прямую загрузку образцов моих книг только участникам моего списка рассылки - вместо этого обычные посетители получают форму подписки.
Майкл Троянек
64

Что случилось с

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

? Я просто попробовал это для рендеринга части HTML ERB из шаблона построителя Atom, и он работал нормально. Не нужно возиться с глобальными переменными (да, я знаю, что перед ними стоит "@", но это то, что они есть).

Тем не менее, ваш with_format &blockподход крутой и имеет то преимущество, что вы указываете только формат, тогда как простой подход также определяет механизм шаблонов (ERB / ​​builder / etc).

Сэм Стоукс
источник
26
Единственным недостатком этого является то, что если ваш партиал рендерит другие партиалы, он потерпит неудачу, если вы не войдете и не измените все свои частичные вызовы рендеринга, включив в их имя ".html.erb".
chrisrbailey
4
вам не нужно указывать механизм шаблонов, чтобы это работало. (По крайней мере, как рельс 3). Следующее работает нормально: render (: partial => "baz.html")
Тим Харпер,
1
Не работает, если у вас разные партиалы для разных языков (например baz.en.html.erb, baz.fr.html.erb) и вам нужна render :partialлогика для выбора правильного (с запасными вариантами и т. Д.).
Джон
1
Что касается rails 3.2.3, при использовании этого решения я вижу следующее предупреждение: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
Одним из недостатков этого подхода является то, что foo.baz.html.[your_string]вместо foo.baz.[your_string]. Ответ zgchurc - лучшее решение.
mbillard
32

Для Rails 3 блок with_format работает, но он немного отличается:

  def with_format(format, &block)
    old_formats = formats
    self.formats = [format]
    block.call
    self.formats = old_formats
    nil
  end
zgchurch
источник
Работает и с рельсами 3.2.0 :)
gucki
32

Rails 4 позволит вам передать параметр форматов. Так ты можешь сделать

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

Обратите внимание, что вы можете сделать что-то подобное в Rails 3, но он не будет передавать этот формат каким-либо подчиненным частям (если форма вызывает другие частичные элементы).

Вы можете получить возможность Rails 4 в Rails 3, создав 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

См. Http://railsguides.net/2012/08/29/rails3-does-not-render-partial-for-specific-format/

DrewB
источник
странно, мне интересно, почему это не работает для меня в приложении Rails 3.2.19, интересно, в чем разница в моем приложении. Невозможно получить ActionView render: partial для соблюдения аргумента: format arg, похоже, не имеет никакого значения, даже с этим патчем.
jrochkind 07
29

Основываясь на ответе roninek , я нашел лучшее решение:

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

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

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

Альтернативным решением было бы переопределение renderдля принятия :formatпараметра.

Я не мог render :fileработать с местными жителями и без некоторой нестабильности.

Джеймс А. Розен
источник
8
слишком хакерский на мой вкус. Лучше указать расширение целиком.
Тим Харпер,
1
Для рельсов 3 см. Ответ zgchurch.
lillq 02
25

В Rails 3 View имеет массив форматов, что означает, что вы можете настроить его на поиск [: mobile,: html]. Установка по умолчанию будет искать: мобильные шаблоны, но вернется к: html-шаблонам. Эффекты от установки этого будут каскадированы во внутренние частичные.

Лучший, но все же ошибочный способ, который я смог найти, - это поместить эту строку вверху каждого полного мобильного шаблона (но не частичного).

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

Недостаток в том, что вам нужно добавить эту строку в несколько шаблонов. Если кто-нибудь знает способ установить это один раз, из application_controller.rb, я бы хотел это знать. К сожалению, добавить эту строку в ваш мобильный макет не получится, потому что шаблоны отображаются до макета.

Тони Стаблбайн
источник
Спасибо за объяснение. В итоге я использовал его в другом месте: stackoverflow.com/questions/47459724/…
Джошуа Пинтер
16

Просто уточняя, что написал zgchurch:

  • с учетом исключений
  • возврат результата вызванного блока

Думал, может быть полезно.

def with_format(format, &block)
  old_formats = formats
  begin
    self.formats = [format]
    return block.call
  ensure
    self.formats = old_formats
  end
end
viphe
источник
10

У вас есть два варианта:

1) использовать render :file

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

2) измените формат шаблона на html, установив переменную @template_format

<% @template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
ронинек
источник
1
<% @template_format = "html"%> работал у меня, так как мои частичные загруженные другие частичные данные мне не приходилось обновлять другие рендеры в цепочке.
pagetribe
5

У меня был файл с именем api / item.rabl, и я хотел отобразить его из представления HTML, поэтому мне пришлось использовать:

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

( fileпотому что у файла нет нижнего подчеркивания в имени, formatsа нет format(а передает и массив))

Дориан
источник
2

Кажется, что передача formatsпараметра будет правильно отображать его в более новой версии Rails, по крайней мере, 3.2:

{
  someKey: 'some value',
  someHTML: "<%= h render('baz', formats: :html) -%>"
}
Марио Угер
источник
1

Я наткнулся на эту тему, когда пытался отрендерить партиал XML в другом файле представления xml.builder. Вот хороший способ сделать это

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

И да ... Полное имя файла здесь тоже работает ...

Гарфилд
источник
Это страдает от той же проблемы с внутренними частями, которую @chrisrbailey упомянул в другом ответе: если частичный вызов, который вы вызываете с полным именем файла, сам использует частичные (без указания полного имени файла для каждого), он потерпит неудачу.
Джеймс А. Розен,