开发者

Rails partial template rendering repeatedly when a helper method uses the yield keyword

开发者 https://www.devze.com 2023-01-25 10:03 出处:网络
I have seen some strange behavior when using rails with partial layouts plus a helper method coded as an iterator using the yield keyword. I am hoping someone can:

I have seen some strange behavior when using rails with partial layouts plus a helper method coded as an iterator using the yield keyword. I am hoping someone can:

  1. Explain 开发者_StackOverflow社区what's going on and why I get the duplicate rendering and maybe
  2. Suggest an alternate approach, hopefully other than just re-coding my helper method to be a simple function that returns a list (I've already done that as an interim workaround)

So if I create the following 3 things in my rails 3 app, I get unexpected output.

[UPDATE] I have tested the following combinations:

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)

So maybe it's an erb vs. haml thing, but when I initially discovered this it was on haml templates. Hmmm....anyone know what's going on???

A) A main template that looks like this (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>

B) A helper method like this (app/helpers/main_helper.rb)

  module MainHelper

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

C) A partial template like this (app/views/main/_partial.html.erb)

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

When I view the result in the browser, I see the "iterator running with" block a total of 8 times (1 2 3 4 1 2 3 4). I have determined it is the yield within my_iterator screwing with the rails partial template mechanism. If I code my_iterator as follows, the output is as I would expect. (I also need to change my partial template to do my_iterator.each)

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

Is there a way to code this such that I don't screw with rails and get duplicate rendering but can still code a helper method as an iterator using yield? Also, can someone explain exactly how the duplicate rendering happens?


I was just having a very similar issue and just managed to solve it. I believe the original helper above would work as expected if rewritten it as such...

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

The same problem seems to occur in Rails 3 if your calling concat(capture(&block)) or concat(block.call). In both cases, just drop the concat() and the duplicate rendering vanishes.


I was experiencing a similar problem which only appeared in production, not development. I narrowed this down to

    config.action_view.cache_template_loading = true

in my development.rb. When set to true, I saw a elements in my haml file. By adding and removing blocks I narrowed down the offending code to

    = if @user.bio
      = @user.bio

which was causing the entire block which preceded this to be re-printed. Simply replacing = with - solves this. It was awkward to debug simply because I had to do in debug in production mode.

My advice is to look for errant = outputs on logical operators.

    - if @user.bio
      = @user.bio


Using content_for within a helper will append content on each iteration. You can write a ApplicationHelper method to yield content within helpers like so:

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

Then use yield_content instead of content_for within your other helper files.

0

精彩评论

暂无评论...
验证码 换一张
取 消