Output out of order when yielding from a partial rendered by Phlex #592
-
That title is a bit of a mouthful, but here's a minimal example. I have a Phlex component that's only job is to render a partial with the provided block. The partial renders some debugging output and yields to the block. # home_component.rb
class HomeComponent < ApplicationComponent
def template(&)
render "home_partial", &
end
end <%# _home_partial.html.erb %>
<p><code>home_partial</code> before yield</p>
<%= yield %>
<p><code>home_partial</code> after yield</p> Then I have an ERB view that renders the partial directly and also renders the component: <div class="prose">
<h2><code>home_partial</code> rendered in ERB view:</h2>
<%= render "home_partial" do %>
<p>content passed to home partial</p>
<% end %>
<hr>
<h2><code>home_partial</code> rendered in Phlex component:</h2>
<%= render HomeComponent.new do %>
<p>content passed to home component</p>
<% end %>
</div> When I view this in a browser, rendering the partial directly works as I'd expect, but rendering the partial within the Phlex component has the output out of order: I understand that these are two different My goal is to gradually integrate Phlex into my app, and this means I'll have lots of Phlex components rendering existing ERB partials and vice versa. My understanding from the docs was that this is supported. Am I doing something wrong? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 25 replies
-
Hey @adamlogic, thanks for the detailed bug report and reproduction steps. You’re not doing anything wrong as far as I can tell, looking at this. It does seem to be a bug. |
Beta Was this translation helpful? Give feedback.
-
I have the same issue in a different scenario: <!-- Response -->
<!-- Before Messages::ShowView -->
<!-- Before ModalComponent -->
<dialog id="modal_message_987" class="modal" data-controller="dialog" data-action="click->dialog#clickOutside">
<div class="modal-box w-full max-w-3x">
<form method="dialog">
<button class="btn btn-sm btn-circle absolute right-4 top-4" data-action="dialog#close">✕</button>
</form>
<h3 class="font-bold text-lg">Angustus compono amaritudo.</h3>
<p class="py-4">Vesco tactus suasoria. Cohaero atqui antepono. Eius praesentium sto.
Architecto comes vereor. Viduata artificiose depono. Sint tenus summisse.
Tubineus armo vesica. Ancilla ab aut. Vorago nobis uredo.</p>
<div class="modal-action">
<form method="dialog">
<!-- Before Modal::ActionComponent -->
<a href="#" class="btn btn-primary mr-2" message-id="987" data-turbo-command="MessageCommand#deliver">Deliver</a>
<!-- Before Modal::ActionComponent -->
<a href="#" class="btn btn-error" message-id="987" data-turbo-command="MessageCommand#destroy">Destroy</a>
</form>
</div>
</div>
</dialog>
<turbo-stream action="invoke" target="DOM">
<template>{"id":"122a5b2b-c0ea-43ec-8c39-84fe690841be","selector":"#turbo-boost","method":"morph","args":["\u003cmeta id=\"turbo-boost\" name=\"turbo-boost\" content=\"IllDQklUaGYwSWlrcnVMQlEi--6a413c759d3ea9609f6d5514ca086b1be99c32abd611a3531a83110ba87a7975\" data-busy=\"false\" data-state=\"e30\" /\u003e"],"delay":0}</template>
</turbo-stream> For the following code: # frozen_string_literal: true
class Messages::ShowView < ApplicationView
include Turbo::FramesHelper
def initialize(message:)
@message = message
super()
end
def template
turbo_frame_tag(:modal) do
_(
:modal,
id: dom_id(@message, :modal),
title: @message.subject,
body: @message.body,
box_class: "w-full max-w-3x"
) do |c|
c.with_action(
title: "Deliver",
href: "#",
class: "btn btn-primary mr-2",
message_id: @message.id,
data: {
turbo_command: "MessageCommand#deliver"
}
)
c.with_action(
title: "Destroy",
href: "#",
class: "btn btn-error",
message_id: @message.id,
data: {
turbo_command: "MessageCommand#destroy"
}
)
end
end
end
end The module ComponentHelper
def component(component_name, ...)
component_class = "#{component_name.to_s.camelize}Component".constantize
render component_class.new(...)
end
alias _ component
end The modal component looks like: # frozen_string_literal: true
class ModalComponent < ApplicationComponent
include Phlex::DeferredRender
attr_accessor :id, :title, :body, :options
def initialize(id:, title:, body:, **options)
@id = id
@title = title
@body = body
@options = options
@actions = []
super()
end
def template
dialog(id: id, class: "modal", data: { controller: "dialog", action: "click->dialog#clickOutside" }) do
div(class: class_names("modal-box", options[:box_class])) do
form(method: "dialog") do
button(class: "btn btn-sm btn-circle absolute right-4 top-4", data: { action: "dialog#close" }) do
"✕"
end
end
h3(class: "font-bold text-lg") { @title }
p(class: "py-4") { @body }
div(class: "modal-action") do
form(method: "dialog") do
@actions.each do |action|
render(action)
end
end
end
end
end
end
def with_action(...)
@actions << Modal::ActionComponent.new(...)
end
end Not entirely sure what is going wrong but this is the only thing blocking me from going hundred percent phlex. |
Beta Was this translation helpful? Give feedback.
Okay, I’ve got a fix here using
capture
to render the block into a string while in Phlex context before passing it down to another Rails renderable. phlex-ruby/phlex-rails#113You should be able test it by setting this in your
Gemfile
:Do you mind if I use your example code as a test case?