DEV Community

NDREAN
NDREAN

Posted on • Updated on

Tabs with Phoenix Liveview

I have a Liveview component with a navbar and components to display/hide as tabs, aka breadcumbs. I also want to highlight the link tag of the active breadcumb.

Say the live endpoint "/crumbs" renders the following Liveview:

# MyAppWeb.CrumbsLive
def renders(assigns) do
  ~H"""
  <section>  
    <Nav active={@active} display={@display} />
    <Elt1.render />
    <Elt2.render />
    ...
  </section>
  """
end
Enter fullscreen mode Exit fullscreen mode

Each tab is a component with a unique id:

# MyAppWeb.Elt1
use Phoenix.Component
def MyApp.Elt1 do
  def render(assigns) do
    ~H"""
    <div id="elt-1">...</div>
    """ 
  end
end
Enter fullscreen mode Exit fullscreen mode

The navbar displays the link to the components. The link will be a patch to the url below as parameter, so we will invoque handle_params (cf the guide https://hexdocs.pm/phoenix_live_view/deployments.html)

"/crumbs?display=elt-i"
Enter fullscreen mode Exit fullscreen mode
  • use the phx-mount dataset to display some folder on mount (available also on static views, not only LVs).

  • use :html to have access to the verified routes

#MyAppWeb.Nav
use MyAppWeb, :html

attr :active, :any  #Function
attr :display, :any #Function

def render(assigns) do
  <nav phx-mount={@display.("#elt-1")}>
    ...
    <.link
      class={@active.("#elt-i")}
      phx-click={@display.("#elt-i")}
      patch={~p"/crumbs?display=elt-i1"}
    >
      Elt i
    </.link>
    ...
  </nav>
end
Enter fullscreen mode Exit fullscreen mode

We pass 2 functions in the assigns:

  • the first prop active is a function that takes the id of the element of the target location. It will add some highlighting classes to the matching location: a simple conditional concatenation of classes if the view given by the query string matches the current location.

  • the second prop display is a function that takes the id of the element of the target location. It will simply pipe JS.hide of all the ids but JS.show the desired one.

A click on the link will be captured by handle_params/3 in the Liveview. We build the props there. Since they depend upon the location, they will be updated into the socket assigns, and this change will render the new UI.

#MyAppWeb.CrumbsLive

def handle_params(q_string, _uri, socket) do
  # list of ids of the tabs/elements
  nav_elts = ['#elt-1', '#elt-2',..]
  base = "some classes"
  styled = "highlighting classes"

  view =
    case map_size(q_string) do
      # on mount
      0 -> "#elt-1"
      _ -> "#" <> Map.get(q_string, "display")
    end

  active = fn current -> [base, current === view && styled] end

  display = fn current ->
      nav_elts 
      |> Enum.filter(&(&1 !== current))
      |> Enum.reduce(%JS{}, fn elt, acc ->
        acc |> JS.hide(to: elt)
      end)
      |> JS.show(to: current)
    end

  {:noreply, assign(socket, active: active, display: display)}
end
Enter fullscreen mode Exit fullscreen mode

Top comments (0)