Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ nav_order: 6

*Cameron Dutro*

* Fix double rendering issue for partials that yield.

*Cameron Dutro*

## 4.0.0

Two years after releasing [3.0.0](https://github.com/ViewComponent/view_component/releases/tag/v3.0.0) and almost six years since [1.0.0](https://github.com/ViewComponent/view_component/releases/tag/v1.0.0), we're proud to ship ViewComponent 4. This release marks a shift towards a Long Term Support model for the project, having reached significant feature maturity. While contributions are always welcome, we're unlikely to accept further breaking changes or major feature additions.
Expand Down
21 changes: 17 additions & 4 deletions lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,30 @@ def render?

# Re-use original view_context if we're not rendering a component.
#
# This prevents an exception when rendering a partial inside of a component that has also been rendered outside
# of the component. This is due to the partials compiled template method existing in the parent `view_context`,
# and not the component's `view_context`.
# As of v4, ViewComponent::Base re-uses the existing view context created
# by ActionView, meaning the current view context and the original view
# context are the same object. set_original_view_context is still called
# to maintain backwards compatibility.
#
# @private
def render(options = {}, args = {}, &block)
if options.respond_to?(:set_original_view_context)
options.set_original_view_context(self.__vc_original_view_context)

# We assume options is a component, so there's no need to evaluate the
# block in the view context as we do below.
@view_context.render(options, args, &block)
else
__vc_original_view_context.render(options, args, &block)
__vc_original_view_context.render(options, args) do
# Partials are rendered to their own buffer and do not append to the
# original @output_buffer we retain a reference to in #render_in. This
# is a problem since the block passed to us here in the #render method
# is evaluated within the context of ViewComponent::Base, and thus
# appends to the original @output_buffer. To avoid this, we evaluate the
# block in the view context instead, which will append to the output buffer
# created for the partial.
__vc_original_view_context.instance_exec(&block)
end
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= render "shared/yielding_partial" do %>
world
<% end %>
2 changes: 2 additions & 0 deletions test/sandbox/app/components/partial_with_yield_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class PartialWithYieldComponent < ViewComponent::Base
end
1 change: 1 addition & 0 deletions test/sandbox/app/views/shared/_yielding_partial.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello <%= yield %>
5 changes: 5 additions & 0 deletions test/sandbox/test/rendering_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1317,4 +1317,9 @@ def test_around_render

assert_text("Hi!")
end

def test_render_partial_with_yield
render_inline(PartialWithYieldComponent.new)
assert_text "hello world", exact: true, normalize_ws: true
end
end
Loading