Template component design - customisable partials

We often load in a very simple form section divider sometimes with supporting text:

with the following code:

    <%= render ComponentView, "form_divider.html",
    title: "Passport",
    supporting_text: "Not required if you are providing other proof of nationality"
    %>

If the requirement comes in for us to style parts of that supporting_text (bold/underline/color-red), then this form_divider component isn’t flexible enough to support that.

This must be a common ‘problem’ and I am just wondering if there is a better design for components to ensure they are more flexible.

I’m currently working on a project with TailwindCSS where I need to do that for other components such as buttons. It seems that the easiest way right now is to abstract the call into a function so you can treat the corner cases you need, for example:

def render_divider(title, text, opts \\ []) do
  # treat opts/ attributes like class, etc and pass it as an assign to the template
  render("divider.html", title: title, text: text, class: opts[:class])
end

PS.: They are working on a new thing called function components that I hope will help with that in the future.

If by “they” you mean Surface, then I think it’s being tracked in this issue. Maybe @msaraiva could chime in with some updates on that?

1 Like

No, I don’t mean Surface. It’s the new Phoenix.Component - although @msaraiva seems to be leading the changes. You can check the roadmap over here.

There’s also something about it in the latest LiveView changelog.

1 Like

Thanks for your answer @thiagomajesk :+1: Although I don’t quite see how it helps me in this case :thinking:

Would you mind adding a little more pseudo code to show how the function helps?

For example, say I wanted the Not required part in bold and I call the function like so:

render_divider("Passport", "Not required if you are providing other proof of nationality", [])

In that case, you could do something like this:

def render_divider(title, do: block) do
  render("divider.html", title: title, content: block)
end

And then call it like so:

<%= render_divider "Passport" do
  <strong>Not required</strong> if you are providing other proof of nationality
end %>

It all depends on what you want/ need to be dynamic. In my first example, I showed you how you can pass custom classes/ styles to the template. But since you actually want to change parts of the content, you need to make it more dynamic - unless of course, you have well-defined blocks.

I recommend that you first think about the building blocks of this component, and then think about what you need to pass down to the template.

You could have multiple templates that solve the same problem, all depending on what you need:

<h1>
  <%= @title %>
</h1>
<hr>
<small>
  <%= @message %>
<small>

Or

<h1>
  <%= @title %>
</h1>
<hr>
<small>
  <strong>
    <%= @callout %>
  </strong>
  <%= @message %>
<small>

In the end, it all depends on how you want to abstract the components.

I think this is what I am looking for :+1:

Thanks a lot, I’ll give it a go