Rendering
What happens when a component is rendered? Quite a bit actually. Let’s have a look.
View template
All Phlex components have the view_template method. This method is where you implement the logic for rendering the component’s HTML.
class Components::Card < Components::Base
def view_template
div do
h1 { "Hello, World!" }
end
end
end
Rendering inside Rails
Rendering the component from an Erb view in Rails might look something like this:
<%= render Components::Card.new %>
The Rails render method calls the #render_in method on the instance of Components::Card.new. This is all still part of Rails conventions. Any instance that responds to render_in will be rendered by Rails, this is how ViewComponent works. In Phlex, it eventually calls #view_template, but first there’s more to it than that.
Rendering before and after hooks
Phlex has rendering hooks that are useful for implementing layouts, debugging code, and more. For example, the base Phlex component in Rails implements a comment before and after the component’s HTML.
class Components::Base < Phlex::HTML
if Rails.env.development?
def before_template
comment { "Before #{self.class.name}" }
super
end
def after_template
comment { "After #{self.class.name}" }
super
end
end
end
This could be useful for debugging in development mode what component emits the HTML, implementing instrumentation, etc.
The around_template hook
A more common hook you’ll use in your application code is the around_template method. This method is called around the view_template method and can be used to implement layout components.
Consider the following card component.
class Components::Card < Components::Base
def around_template
super do
div(class: "card") do
yield # Renders the `view_template`
end
end
end
def view_template
div do
h1 { "Hello, World!" }
end
end
end
In this example, we wrap the view template in a div tag with the card class. There’s a lot going on here. The super method calls the around_template method from the superclass. If we left that out, we wouldn’t get the HTML comments we implemented in the Components::Base superclass.
The yield block is where the view_template is rendered. It’s a block that is passed to the around_template method and is called within the block.
The around_template hook is useful for implementing layouts for components.
The Components::Card we implemented isn’t all that useful since we’ve hard coded the h1 tag with the Hello, World! text. Ultimately what we want is a way to wrap any HTML content inside a Components::Card, which we can achieve with blocks.
class Components::ContactCard < Components::Card
def initialize(user:)
@user = user
end
def view_template
h1 { @user.name}
h2 { "Contact Information" }
dl do
dt { "Name" }
dd { @user.name }
dt { "Email" }
dd { @user.email }
end
end
end
When this renders, the contents of the view_template are wrapped from the div tag in the around_template method from the Components::Card superclass. There’s a better way to implement layouts though, which we’ll get into in a later chapter.
Rendering is polymorphic
The render method in Phlex is quite capable. You can give it blocks, procs, methods and as long as there’s Phlex inside, it will render it.
Let’s generalize the Components::ContactCard component by giving it a title method that can be called.
class Components::TitleCard < Components::Card
def title = nil
def subtitle = nil
def around_template(&block)
super do
# Either way works
h1 { title }
h2 &:subtitle
render &block
end
end
end
Now let’s re-implement the Components::ContactCard component using the Components::TitleCard component.
class Components::ContactCard < Components::TitleCard
def initialize(user:)
@user = user
end
def title = @user.name
def subtitle = "Contact Information"
def view_template
dl do
dt { "Name" }
dd { @user.name }
dt { "Email" }
dd { @user.email }
end
end
end
So far nothing too special, the title and subtitle methods return strings that Phlex renders, but what if we want the title to link to the user’s profile page?
class Components::ContactCard < Components::TitleCard
def initialize(user:)
@user = user
end
def title
a(href: user_path(@user)) { @user.name }
end
def subtitle = "Contact Information"
def view_template
dl do
dt { "Name" }
dd { @user.name }
dt { "Email" }
dd { @user.email }
end
end
end
In this example, the title string was replaced with calling Phlex methods. This changes out the plain text it rendered with the full markup afforded by Phlex.