I have a partial that I am trying to render content inside of
<!-- app/views/shared/_yes_no_toggle.html.erb -->
<div data-controller="yes-no-toggle">
<div class="flex items-center">
<button type="button" data-action="click->yes-no-toggle#onYes" class="bg-blue-500 text-white px-4 py-2 rounded-l rounded-r-none text-xs" aria-labelledby="yes-no-label">Yes</button>
<button type="button" data-action="click->yes-no-toggle#onNo" class="bg-red-500 text-white px-4 py-2 rounded-r rounded-l-none text-xs" aria-labelledby="yes-no-label">No</button>
<label class="ms-2" id="yes-no-label">Include Core Funded Services?</label>
</div>
<div data-target="yes-no-toggle.yesView" class="mt-4 hidden">
<%= yield :yes_content %>
</div>
<div data-target="yes-no-toggle.noView" class="mt-4 hidden">
<%= yield :no_content %>
</div>
</div>
However, when I try to render this view within another rendered view, the named <%= yield :yes_content %>
won't work unless I add another yield tag on top:
<!-- app/views/shared/_yes_no_toggle.html.erb -->
...
<div data-target="yes-no-toggle.yesView" class="mt-4 hidden">
<%= yield %>
<%= yield :yes_content %>
</div>
<div data-target="yes-no-toggle.noView" class="mt-4 hidden">
<%= yield %>
<%= yield :no_content %>
</div>
...
This is being called inside another partial
<!-- app/views/shared/example/_form.html.erb -->
<%= render 'shared/yes_no_toggle' do %>
<% content_for :yes_content do %>
<span>Hello World</span>
<% end %>
<% content_for :no_content do %>
<span>Goodbye World</span>
<% end %>
<% end %>
Can someone help me understand what's happening here? I have gone through the documentation to understand how it may be working but I'm a bit stumped. I've tried adding the content_for
below the proposed render but no luck.
Without plain yield
the block that you pass to render 'shared/yes_no_toggle'
is never executed which means content_for
is never populated with anything.
The actual render
is a lot more complicated, this is as simple as I could make it in terms of actual logic underneath:
def content_for name
@content_for ||= {}
if block_given?
@content_for[name] = yield
else
@content_for[name]
end
end
def render template
# calls _yes_no_toggle method
send(template) do |content|
if content
content_for(content)
else
yield if block_given? # here <---------------.
end # |
end # |
end # |
# |
def _yes_no_toggle # |
# NOTE: without yielding >-------------. |
# yield # | |
# | |
"<div>" + # | |
yield(:yes_content).to_s + # | |
yield(:no_content).to_s + # | |
"</div>" # | |
end # | |
# | |
def _form # | |
render :_yes_no_toggle do # <-' this block |
content_for :yes_content do # is never called -'
"<span>Hello World</span>" #
end #
content_for :no_content do #
"<span>Goodbye World</span>" #
end #
end
end
# without yield
_form
#=> "<div></div>"
# with yield
_form
#=> "<div><span>Hello World</span><span>Goodbye World</span></div>"
Also, remember that yield(:yes_content)
is the same as content_for(:yes_content)
maybe that will clear up confusion with plain yield
.