Super flexible Phlex components
Phlex is a Ruby gem for building fast object-oriented HTML and SVG components.
Unlike traditional erb
, where you write html interwoven with ruby, Phlex allows you to write your components with plain ol' ruby, and if you've used ruby, I don't have to tell you the possibilities this opens.
In this article, I want to show you how you can make a super flexible component using a technique I call phlexi rendering.
Let's take a very simple PageHeader component that has a title and a description.
class PageHeader < Phlex::HTML
def initialize(page_title:, page_description:)
@page_title = page_title
@page_description = page_description
end
def view_template
div(class: "flex-row items-center justify-between space-y-3 sm:flex sm:space-y-0 sm:space-x-4 mb-6") {
div {
h2(class: "mb-2 text-3xl font-extrabold leading-none tracking-tight text-gray-900 md:text-4xl dark:text-white") {
@page_title
}
p(class: "text-gray-500 dark:text-gray-400") {
@page_description
}
}
}
end
end
This works fine, buuuuuuut, we are limited to only rendering strings. What if I want this one page to use something different?
Am I limited to recreating the PageHeader for this page? What if it is part of a layout?
phlexi rendering to the rescue! By defining this method, you can pass strings, blocks or even another component.
def phlexi_render(arg, &)
return unless arg
raise ArgumentError, "phlexi_render requires a default render block" unless block_given?
# Handle Phlex components or Rails Renderables
if arg.class < Phlex::SGML || arg.respond_to?(:render_in)
render arg
# Handle procs
elsif arg.respond_to?(:to_proc)
instance_exec(&arg)
else
yield
end
end
First, let's modify our component's view template to use phlexi_render
for the description.
def view_template
div(class: "flex-row items-center justify-between space-y-3 sm:flex sm:space-y-0 sm:space-x-4 mb-6") {
div {
h2(class: "mb-2 text-3xl font-extrabold leading-none tracking-tight text-gray-900 md:text-4xl dark:text-white") {
@page_title
}
phlexi_render(@page_description) {
p(class: "text-gray-500 dark:text-gray-400") {
@page_description
}
}
}
}
end
Then we create our custom description component.
class CustomDescriptionComponent < Phlex::HTML
def view_template
div(class: "mt-4 gap-4 flex") do
p(
class:
"text-2xl font-extrabold text-gray-900 sm:text-3xl dark:text-white"
) { " $1,249.99" }
div(class: "flex items-center gap-2 mt-2 sm:mt-0") do
a(
href: "#",
class:
"text-sm font-medium leading-none text-gray-900 underline hover:no-underline dark:text-white"
) { " 345 Reviews " }
end
end
end
end
Now, we can render our page header like this
render PageHeader.new(
title: "Special purpose vehicles",
description: CustomDescriptionComponent.new
)
You can also pass in procs which will be executed in the context of the PageHeader component like this:
render PageHeader.new(
title: "Special purpose vehicles",
description: -> {
p { "This is a callable description" }
}
)
And that's it! I hope you liked this trick.
Stay Phlexi!
Subscribe to my newsletter
Read articles from TheDumbTechGuy directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by