kuon

kuon

I am using live view to render a table:

  <table>
    <thead>
      <tr>
        <th>#</th>
        <%= for c <- @columns do %>
        <th>
          <%= tr(c.display_name, 32) %>
        </th>
        <% end %>
        <th></th>
      </tr>
    </thead>
    <tbody>
      <%= for {r, rnum} <- @rows_index do %>
      <tr class="<%= if rnum == @selected_y do "selected" end %>">
        <td><%= rnum + 1 %></td>
        <%= for {val, cnum} <- r  do %>
        <td phx-click="select_cell"
          phx-value-x="<%= cnum %>"
          phx-value-y="<%= rnum %>"
          class="<%= if cnum == @selected_x && rnum == @selected_y do "selected" end %>">
          <%= val %></td>
        <% end %>
        <td></td>
      </tr>
      <% end %>
    </tbody>
  </table>

I have about 150 columns and 10 000 rows.

The problem is that when I do the select_cell call, and update selected_x and selected_y the update takes a few seconds.

Is there a way to optimize this with live view, or should I move to JS?

Showing Posts 1 to 10

chrismccord

chrismccord

Creator of Phoenix

You should use temporary assigns for the rows and you can either use phx-update="append" and append the two updated rows (the old row which is now unselected and the new selected row), or you can use a component for reach row, and do a send_update to the old selected row, and newly selected row. Both options will send the minimal diff down and not require holding 10k rows in your server state.

kuon

kuon OP

If I get this right, I should:

  • move data to temporary assigns
  • create a row component
  • put phx-target to myself on the row component and handle the event in the row component
  • in the event handler, I use pubsub to broadcast the row change selection
  • in handle_info of the live view, I send_update to the two affected rows

Is that the way to go?

chrismccord

chrismccord

Creator of Phoenix

You don’t need the pubsub step and instead of targeting the component, let the parent LV handle the event, then it performs the send_update to both the component children. The parent LV has the selected state, so it can use that to send_update the selected component, then the handle_event params can contain the desired selected row, which you will use to send_update the newly selected component. Make sense?

kuon

kuon OP

Yes, it is working with send_update from the live view.

I did create a component for each rows, but with large tables (>100 columns), even updating a single row takes a noticeable time.

Is it possible to nest components? Like having a component for the row that would just render <tr class="..."> and only handle the switch of class on the the row while using a for loop to render sub components? Would that just work?

Also, I have a “tab” selector above my tables, and when I change tab, I need to re-render the whole table. Right now, live view is sending a “huge” diff with every rows when changing tabs. Is it possible to tell live view to just re-render the container and replace the whole container content in one operation?

50kudos

50kudos

I have quite similar application, using send_update can get hairy at some point (like jQuery approach, sometimes it needs other components updated too e.g. resetting [ui] state for the rest, and only update a component)

I have a very big tree component (recursively rendered), so yes nested components is possible. However, the way out often is rethinking of state design. It’s quite tricky to get minimal diff payload for large DOM nodes.

I am moving towards more pagination, less DOM nodes is better. For the “tab”, you could possibly remodeling your assigns, moving assigns that make it marked as “changed” out, when switching tab 1 → tab 2 → tab 1 (this stays unchanged), but if you want always fresh data, I don’t think there is a way around other than reducing DOM nodes. (And I think toString on javascript side is not that performant .. something around Array.from that is slow)

Also be aware of memory usage per process (per browser tab) too!

kuon

kuon OP

I tried to have a top component, like this:

... here html for a tab selector that change @table assign

  <%= live_component @socket,
        CardioDataWeb.DBLive.TableComponent,
        id: :main,
        table: @table,
        sel: @sel
  %>

This above component render a

tag with thousands of cells.

Now, when I change the @table in the top component, instead of just having 1 single big diff like wrapper.innerHTML = "some big chunk of html" I have thousands and thousands of diffs which takes tens of seconds to be applied. (if I render the HTML normally without live view, rendering in the browser is instant)

My tab selector could be regular links, but I want to keep the state between tab changes.

My use case is some sort of spreadsheet editor. I could use some JS lib, but I tested native HTML rendering, and everything is butter smooth with thousands of rows, browsers are really good at managing large pages and I thought I could leverage this with live view, but live view needs to have minimal diff.

kuon

kuon OP

I also tried nesting live views, but when the child live view is unmounted/remounted, there is also a huge diff which takes a very long time to be processed (much longer than just doing similar to innerHTML = newdata).

50kudos

50kudos

My untested idea is that you could do something like what infinite twitter feed does, only render rows fit within screen viewport, not sure if it would be clunky or not. For example, 300 rows. The downside is native search on browser will suck.

That’s the only solution (pagination) I stick with, I gave up minimizing diff payload in other way since it’s going to have at least a bunch of one character keys (a lot of them alone without content is huge)

Or just give up view layer on server for those cells, just push_event with pure data and use whatever lib to render the cells on client.

50kudos

50kudos

Also I guess your sel: @sel sit on every row (somewhere like <%= if @sel, do: "" %>), its change always mark the whole table (i.e. every row) as change and produce diff payload of every row even if @table data is unchanged.

kuon

kuon OP

Well “smart partial client side rendering” is possible, but I want to avoid that because rendering the whole table works very well even with huge table.

And I don’t want to minimize diff size, what I’d like is to have a single large diff for the table when the table change, not a million small diffs for all element in the table.

As I said if I re-render the whole table, I’d like the container’s content to be swapped with the new content, in one single operation. I don’t want a million diff be applied, which is very slow.

So I am wondering if this is possible with live view.

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
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

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews