Surface render slot of child component

Hi,

I’m trying to create a Tab component with TabItem children using Surface. Currently it looks like this:

Tabs.ex

defmodule DoomWeb.Components.Tabs.Tabs do
  use Surface.LiveComponent

  prop(initial, :atom, required: true)

  data(selected_tab, :atom, default: nil)

  slot(tabs)

  def mount(socket) do
    {
      :ok,
      socket
      |> assign(:selected_tab, :plans)
    }
  end

  def handle_event(event, params, socket) do
    case event do
      n when n == "change_tab" ->
        on_change_tab(params, socket)

      _ ->
        {
          :noreply,
          socket
        }
    end
  end

  defp on_change_tab(%{"tab" => tab_id}, socket) do
    {
      :noreply,
      socket
      |> assign(:selected_tab, String.to_atom(tab_id))
    }
  end

  def render(assigns) do
    ~F"""
    <div class="flex flex-wrap w-full">
        <ul class="flex mb-0 list-none flex-wrap pb-0 flex-row border-b-1">
          {#for tab <- @tabs}
            <li
              class="-mb-px mr-2 last:mr-0 flex-auto text-left"
              :values={tab: tab.id}
              :on-click="change_tab"
            >
              <a class="text-md font-bold px-0 rounded-t block leading-normal cursor-pointer text-skin-base">
                {tab.title}
              </a>
            </li>
          {/for}
        </ul>

              {#for tab <- @tabs}
                <div class="block p-2 text-sm" :if={@selected_tab == tab.id}>
                  <#slot />  <-------- HERE how can i access tab item .slot??
                </div>
              {/for}
</div>
    """
  end
end

TabItem.ex

defmodule DoomWeb.Components.Tabs.TabItem do
  use Surface.Component, slot: "tabs"

  prop(id, :atom, required: true)
  prop(title, :string, required: true)
  prop(icon, :string)

  slot(default)
end

Then I can use it:

<Tabs id="subscription" initial={:plans}>
          <TabItem id={:plans} title={gettext("Plans")}>
            plans
          </TabItem>

          <TabItem id={:billing_history} title={gettext("Billing History")}>
            billing history
          </TabItem>

          <TabItem id={:payment_method} title={gettext("Payment Method")}>
            payment method
          </TabItem>
        </Tabs>

My question is how can I access inside the for expression on the Tab.ex the slot of each of the TabItem, so that I can render them?

No ideas?

don’t know about Surface, but in plain LV it would probably be:

<%= render_slot(tab) %>

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Helpers.html#render_slot/2

It should be:

<#slot for={tab}/>
1 Like