herisson

herisson

Hi everyone,
I’ve been trying liveview for the past few days and I found it pretty straight forward when building simple pages.
However, I am now trying to build a SAAS-like dashboard but I’m having a hard time figuring out how to manage it with liveview.

IE:

As I see it there are two way to handle this:

  1. Using a layout dashboard.html.heex:
    This method would mean that for each time I click a tab, I render a new liveview. Reloading the entire layout? How would my layout know on which tab I currently am?

  2. Using a single liveview:
    This one would I think look similar to how phx.gen.live handle different views, but considering a large app with multiple tabs, would the file not contain too much ?

Maybe I am missing something. How would you guys build something like that ?
Thanks for the help!

Showing Posts 1 to 6

sbuttgereit

sbuttgereit

Have you looked at:

https://github.com/phoenixframework/phoenix_live_dashboard

It seems to be very similar to the kind of problem you’re trying to solve.

herisson

herisson OP

Yes I have but it seemed like an overcomplicate solution. Maybe it’s not and I have to look into it a bit more.
Anyway, thanks for the answer

GazeIntoTheAbyss

GazeIntoTheAbyss

The easiest method is given as default I believe.

Router Method

Router:

        live "/dashboard", DashboardLive.Index, :index
        live "/team", DashboardLive.Index, :home

DashboardLive.Index

--- Default Handle Params ---
  def handle_params(params, _, socket) do
    {:noreply,
     socket
     |> apply_action(socket.assigns.live_action, params)     
    }
  end

--- Actions Matching Your Router Atoms ---
  defp apply_action(socket, :index, params) do
    socket
    |> assign(:page_title, "Index")
  end

  defp apply_action(socket, :home, params) do
    socket
    |> assign(:page_title, "Home")
  end

Index.html.heex

<%= if @live_action in [:index] do %>
--- Content For Index ---
<% end %>

<%= if @live_action in [:home] do %>
--- Content For Homr ---
<% end %>

For the above, the apply_action values of :index and :home within the DashboardLive.Index file are paired with the paths in your Router.

live "/dashboard", DashboardLive.Index, :index

When you go to /dashboard you run:

  defp apply_action(socket, :index, params) do
    socket
    |> assign(:page_title, "Dashboard")
  end

The handle_params allows you to use <%= if @live_action in [:whatever] %> to display content specific to the Router path as it applies the action to the socket.

Non Router Method

If you want to display content without using different router paths, you can still do similar.

In your mount, assign a default value to the socket.

  def mount(_params, _session, socket) do   
    socket = socket
      |> assign(:page, "dashboard")
    {:ok, socket}
  end

You can display any dashboard based content by using:

<%= if @page === "dashboard" do %>

<% end %>

To change the content, when a button is clicked you trigger a handle_event and update the socket value for page instead of changing path

Home button on the dash.

<.button type="button" phx-click="change content" phx-value-page="home" >
    <%= "Home Button %>
</.button>

Handle_Event to update the socket. The “page” => page will take your phx-value-page string value and assign it to the socket as :page. Effectively you are change :page from “dashboard” to “home”

  def handle_event("change conten", %{"page" => page}, socket) do
    {:noreply, socket |> assign(:page, page)}
  end

Once the socket has been updated the @page value will now be “home” so the original content being displayed as “dashboard” will be hidden, and the below will be displayed.

<%= if @page === "home" do %>

<% end %>

The handle_event may need something like patch to reload the content though, not sure how my example actually performs as I obviously didn’t test it.

I’m just a humble amateur :slight_smile: but I believe the Router method is prefered for full page loads, and the non Router method is more for showing/hiding things on the page or passing values around.

herisson

herisson OP

This was actually my first attended method however once the page grows, it becomes quite messy.
I’ll give it a try It might work !

GazeIntoTheAbyss

GazeIntoTheAbyss

Are you familiar with livecomponents yet? Disregard the below if so, but figured I’d share as I find it a neat way to deal with lots of pages and am currently working on a dashboard as well.

Livecomponents can make large projects much more manageble, allow you to create reuseable code blocks and can be nested within one another.

I don’t know if this is good/bad/common practice but its very neat and easy to follow if you have a lot of routes/paths in a dashboard set up. Rather than creating a wall of code in the Index, you create each page seperately as a component then just call upon it within the index.

<%= if @live_action in [:users] do %>
            <.live_component
              module={APPWeb.AdminUserComponent}
              id={"admin-user-component"}
              page_title={@page_title}
              action={@live_action}
              users={@users}
              pagination={@pagination}
            />
          <% end %>

          <%= if @live_action in [:edit_user] do %>
            <.live_component
              module={APPWeb.AdminUserEdit.FormComponent}
              id={"user-edit"}
              action={@live_action}
              user={@user}
            />
          <% end %>

        <!-- Groups -->

          <%= if @live_action in [:groups, :create_group, :edit_group] do %>
            <.live_component
              module={APPWeb.AdminGroupComponent}
              id={"admin-groups-component"}
              page_title={@page_title}
              action={@live_action}
              groups={@groups}
              pagination={@pagination}
            />

            <%= if @live_action in [:create_group] do %>
              <.live_component
                module={APPWeb.AdminGroupCreate.FormComponent}
                id={"admin-groups-create"}
                page_title={@page_title}
                action={@live_action}
                live_search={@live_search}
                admin={@admin}
                group={@group}
                groups={@groups}
                parent={@parent}
                selected_group={@selected_group}
                group_type={@group_type}
                group_tree={@group_tree}
                top_level_groups={@top_level_groups}
              />
            <% end %>

            <%= if @live_action in [:edit_group] do %>
              <.live_component
                module={APPWeb.AdminGroupEdit.FormComponent}
                id={"admin-groups-edit"}
                page_title={@page_title}
                action={@live_action}
                live_search={@live_search}
                admin={@admin}
                group={@group}
                groups={@groups}
                parent={@parent}
                group_type={@group_type}
                group_tree={@group_tree}
                selected_group={@selected_group}
                top_level_groups={@top_level_groups}
                patch={~p"/mod-zone/edit_group"}
              />
            <% end %>
          <% end %>
adw632

adw632

I would suggest reading Live Navigation docs.

You don’t need to make a complex multiheaded hydra liveview with different actions using live components. Given this creates additional complexities with data loading for the different action cases in the parent liveview I would recommend against this approach if you have vastly different data requirements between each live component.

Instead, where the data requirements are different you can use distinct routes within your live_session block in your router config with independent live views and just use <.link navigate={...}> from your navigation menu to switch between them. As per the documentation, it does not re-render the entire layout.

It looks like you are using Petal components. There may be some additional considerations with live navigation from their menu component as you want live navigation, not redirection with full page reload.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews