HEEx: Pass through all attributes?

I have the following utility template, for a list that is required to be rendered as a grid whenever CSS is enabled:

<%!-- grid_list.html.heex -->
<ul
  class={[
    "tw-grid",
    "tw-grid-cols-#{Enum.count(@entries) |> :math.sqrt |> :math.ceil |> trunc}",
    @class
  ]}
>
  <li :for={entry <- @entries}>{render_slot(@inner_block, entry)}</li>
</ul>

<%!-- https://tailwindcss.com/docs/detecting-classes-in-source-files#dynamic-class-names
"tw-grid-cols-1"
"tw-grid-cols-2"
"tw-grid-cols-3"
"tw-grid-cols-4"
"tw-grid-cols-5"
"tw-grid-cols-6"
"tw-grid-cols-7"
"tw-grid-cols-8"
--%>

I invoke it, for example, as so:

<.grid_list :let={asset} entries={@state.assets} class="tw-p-1 tw-gap-1 sm:tw-p-4 sm:tw-gap-4">
  <.asset asset={asset}/>
</.grid_list>

So my question is: is there any way for me to have the template automatically pass through any “extra” attributes, such as title, that I invoke it with? Or do I have to hardcode the “whitelist” of allowed passthrough attributes?

You can use global attributes. There is a whitelist, but the default includes all HTML global attributes (title is one of those). You can explicitly whitelist others in the attr call.

IMO, a better option is to just use a style tag:

style={“grid-template-columns: repeat(#{Enum.count(@entries*) …}, minmax(0, 1fr));”}

Hmm, it looks like “key :rest not found” is the result if I add a <... title="..."> parameter to the invocation and a <... {@rest}> parameter inside the template.

Putting attr :rest, :global just before embed_templates "page_html/*" also leads to some incomprehensible compile-time errors on utility templates that used to be fine:

could not define attributes for function asset/1. Please make sure that you have use Phoenix.Component and that the function has no default arguments


Is there any way to configure Phoenix.Component.attr/3 within .heex templates, or will I have to refactor this to use .ex/def (assigns)/~H?

I think you do have to do this, yes. We’ll see if somebody corrects me.

Some people disable inline styles via CSP for security reasons. There are ways around this but they’re annoying.

When you want to use attr / slot with an external .heex file you need to define an empty function head in the .ex file that has embed_templates. There’s an example in the docs: Phoenix.Component — Phoenix LiveView v1.1.11

2 Likes