Controlling conditional slot rendering in the component

Let’s say I have a slot that is conditionally rendered based on a parameter:

<.component params={[...]}>
  <:slot :let={param}>
    <p :if={param}>...</p>
  </:slot>
</.component>

And in my component:

<%= for param <- @params do %>
  <div class="..."><%= render_slot(@slot, param) %></div>
<% end %>

Can I suppress the rendering of the entire <div> element in the component based on whether the slot has any content?

No, that would be a cyclic dependency. The outer component needs to render the slot to know the param, but the params would control if the slot is rendered.

You could only hardcode that within the component or pass in the computation you do based on param, so the component can execute it before rendering the slot.

I don’t see why it’s cyclical. Slot definition does not depend on the component. For example, a possible solution might be something like this:

<%= for param <- @params do %>
  <% s = prerender_slot(@slot, param) %>
  <div :if={s} class="..."><%= render_prerendered(s) %></div>
<% end %>

Why not just this?:

<%= for param <- @params do %>
  <div :if={param} class="..."><%= render_slot(@slot, param) %></div>
<% end %>

Example does not indicate any requirement on why you need to do this check on the parent component.

If you absolutely need to, then it should be sth like this:

  <%= for param <- @params do %>
    <div :if={hd(@slot) && hd(@slot).__inner_block__} class="..."><%= render_slot(@slot, param) %></div>
  <% end %>

But I don’t suggest it. Whenever I find myself in a similar situation with slots, then I realize there is always a much simpler way to achieve the same result.

You are assuming the component knows the :if condition in the caller. But the condition is arbitrary, so :if={param} is really :if={some_condition(param)}.

So thinking about your response, I think I figured out the answer:

<.component params={[...]} render_slot?={fn param -> some_condition(param) end}>
  <:slot :let={param}>
    <p >...</p>
  </:slot>
</.component>

Component:

<%= for param <- @params do %>
  <div :if={render_slot?.(param)} class="..."><%= render_slot(@slot, param) %></div>
<% end %>
2 Likes