Adding Parameterized Layouts to phlex-rails
I believe the hack described in this post broke due to changes in Phlex. The good news is that the latest version of Phlex supports parameterized layouts now, so this whole post is moot.
As of publishing, phlex-rails doesn’t allow you to parameterize your layout components. There’s a reason for it concerning how Rails handles rendering layouts. Instead, they recommend using content_for
and yield
. This works fine for some use cases, but it doesn’t work great when you’re converting a layout that uses instance variables to control aspects of the layout.
You can bring your own parameterized layouts by adding the following code to your ApplicationLayout
class:
class Wrapper
def initialize(klass:, args:, kwargs:)
@klass = klass
@instance = klass.new(*args, **kwargs)
end
def render(view, _locals, &block)
@instance.call(view_context: view) do |yielded|
case yielded
when Symbol
output = view.view_flow.get(yielded)
else
output = yield
end
case output
when ActiveSupport::SafeBuffer
@instance.unsafe_raw output
end
nil
end
end
def identifier
@klass.identifier
end
def virtual_path
@klass.virtual_path
end
end
def self.with(*args, **kwargs)
Wrapper.new(klass: self, args: args, kwargs: kwargs)
end
With this, the standard approach to selecting your layout will still work.
layout -> { ApplicationLayout }
When you need to parameterize your layout, use the with
method instead.
layout -> { ApplicationLayout.with(theme: :blue, show_header: false) }
I hope phlex-rails will eventually support this use case, but this snippet solves the problem for now.