Rails частичный рендеринг шаблона несколько раз, когда вспомогательный метод использует ключевое слово yield

Я видел странное поведение при использовании рельсов с частичной разметкой плюс вспомогательный метод, закодированный как итератор с использованием ключевого слова yield. Я надеюсь, что кто-то может:

  1. Объясните, что происходит и почему я получаю дубликат рендеринга и, возможно,
  2. Предложите альтернативный подход, надеюсь, помимо простого перекодирования моего вспомогательного метода в простую функцию, которая возвращает список (я уже сделал это в качестве временного обходного пути)

Поэтому, если я создаю следующие 3 вещи в своем приложении rails 3, я получаю неожиданный вывод.

[ОБНОВЛЕНИЕ] Я проверил следующие комбинации:

Rails 3.0.0 + erb (has this issue)
Rails 3.0.0 + haml (OK)
Rails 3.0.3 + erb (has this issue)
Rails 3.0.3 + haml (OK)

Так что, может быть, это что-то типа эрб против хамла, но когда я впервые обнаружил это, это было в хамл-шаблонах. Хммм.... кто-нибудь знает, что происходит???

A) Основной шаблон, который выглядит следующим образом (app/views/main/index.html.erb)

  <h1>Main#index</h1>
  <p>This is content from main#index before the partial template rendering
  <%= render :partial => "partial" %>
  <p>This is content from main#index after the partial template rendering.</p>

Б) вспомогательный метод, подобный этому (app / helpers / main_helper.rb)

  module MainHelper

    def my_iterator
      yield 1
      yield 2
      yield 3
      yield 4
    end
  end

C) Частичный шаблон, подобный этому (app/views/main/_partial.html.erb)

  <% my_iterator do |x| %>
  <p>iterator running with <%= x %></p>
  <% end %>

Когда я просматриваю результат в браузере, я вижу блок "Итератор работает с" в общей сложности 8 раз (1 2 3 4 1 2 3 4). Я определил, что это доходность в завинчивании my_iterator с механизмом частичного шаблона рельсов. Если я кодирую my_iterator следующим образом, вывод будет таким, как я ожидал. (Мне также нужно изменить мой частичный шаблон, чтобы сделать my_iterator.each)

def my_iterator
  logger.debug("my_iterator called")
  return [1, 2, 3, 4]
end

Есть ли способ закодировать это так, чтобы я не привинчивался к рельсам и не получал дублирующийся рендеринг, но все еще мог закодировать вспомогательный метод как итератор, использующий yield? Кроме того, кто-то может объяснить, как именно происходит дублирование рендеринга?

3 ответа

Решение

У меня была очень похожая проблема, и мне удалось ее решить. Я полагаю, что оригинальный помощник выше будет работать так, как ожидалось, если переписать его как таковой...

module MainHelper
  def my_iterator(&block)
    block.call(1)
    block.call(2)
    block.call(3)
    block.call(4)
  end
end

Похоже, такая же проблема возникает в Rails 3, если ваш вызов concat(capture(&block)) или же concat(block.call), В обоих случаях просто бросьте concat() и дублирование рендеринга исчезает.

У меня была похожая проблема, которая появилась только в производстве, а не в разработке. Я сузил это до

    config.action_view.cache_template_loading = true

в моем развитии. Когда установлено значение true, я увидел элементы в моем файле haml. Добавляя и удаляя блоки, я сузил код ошибки до

    = if @user.bio
      = @user.bio

который заставлял весь блок, который предшествовал этому, быть перепечатан. Простая замена = на - решает это. Отладка была неловкой просто потому, что мне приходилось делать отладку в производственном режиме.

Мой совет - искать ошибочные = выходы для логических операторов.

    - if @user.bio
      = @user.bio

Использование content_for внутри помощника будет добавлять контент на каждой итерации. Вы можете написать метод ApplicationHelper для получения содержимого в помощниках следующим образом:

def yield_content(content_key)
  view_flow.content.delete(content_key)
end

Затем используйте yield_content вместо content_for в других ваших вспомогательных файлах.

Другие вопросы по тегам