ryanzidago

ryanzidago

What’s the canonical way of implementing an editable tabular data / grid / table in Phoenix LiveView?
Think of a spreadsheet but only with a minimal feature set (inline editing, showing select inputs, errors, validations, suggestions, etc.).

I don’t really need multi-collaboration in real time for now.

I have to deal with anywhere between 300 - 600 rows of 30 - 50 columns and probably more in the future.

I have tried the following approaches:

1) Streams + HTML table + form per row

HTML does not allow to have a form per row so it does not work. If I’m using streams, I need to have a form per row so on input change, the Live View gets all the data for the given row, not just for the cell being currently edited.

2) Streams + HTML table + form per table using form attribute on form input

Could not make the change tracking work. The form just sends empty params.

3) Streams + HTML table + form per cell

  • the form will only send one field at a time (except for the field where I explicit add phx-value_some_field but I’d need to repeat that over each input for all of the fields of the record I want to update…)
  • and then in the handle_event("validate"), I would have to do the following:
def handle_event("validate", params, socket) do
  changesets = socket.assigns.record_changesets
  changeset = Enum.find(changesets, &(get_field(&1, :id) == params["id"])
  changeset = Record.changeset(changeset.data, params)
  socket = stream_insert(:changesets, changeset)
  {:noreply, socket}
end

Meaning holding the collection in both the assigns and in the stream, which seems to defeat the purpose of streams in the first place.

4) Streams + indivual form input without any forms

So I have:

<tr>
  <td><.input type="text" value={get_field(changeset, :some_field)} phx-change="validate" phx-value_id={get_field(changeset, :id)} .../></td>
  <td><.input type="select" options={@options} value={get_field(changesetm :some_other_field)} phx-change="validate" phx-value_id={get_field(changesetm :id))} /></td>
</tr>

The problem with stream_insert is that I lose the focus on the cell that was currently being edited. All of the examples that I could find about streams seems to point out that when you want to edit a streamed item, you open a modal, go to another route, etc … I have not seen many use-case for inline editing, which makes me wonder if I am supposed to use streams for that at all.

5) CSS grid

CSS grid does not seem fit for purpose for tabular data. You can’t wrap your headers / subheaders or rows into a parent element and apply classes to the children so it becomes hard to have stuff like highlight the entire row on cell hover. I’m concerned about using CSS grid especially when requirements will grow in complexity in the future.

6) LiveView + JS library (AG-Grid, Handsontable, etc.) via Phoenix hook

Those are either expensive, or I need to duplicate some of the business logic over to JavaScript, which I’d like to avoid as it seems hard to maintain over time.

Showing Posts 1 to 9

BartOtten

BartOtten

A post from mobile…so grain of salts are advised.

Replies to listed solutions.

solution 1: How about CSS with display: table? You can wrap divs in one form, no?

Solution 4: You can also add JS hooks to save cursor position and re-activate the cell after stream-insert.


Just freewheeling

assuming you also need some column aggregation / sums.

Create an matrix with input fields with a col-class and row-class. Example class=“b 2”.

Have an event handler send the values to LiveView once a value is entered. Examples just for illustration.

Number 8 in “c 2”: {{c, 2}, :equals, value: 8}
Sum column “g” in “c 3”: {{c, 3}, :calc, [{:sum, {g, nil}}]}

This assumes the whole table is in state and you can query and patch it.

If you don’t want to have state on the server, you can afaik only resort to much more (custom) JS or lazy load rows and cols after dropping the whole table after initial render (but then it gets very complicated im afraid)


That being said: this seems like a task outside the optimal scope for LiveView.

BartOtten

BartOtten

The million dollar question: does column data matter or are we only talking rows?

sreyansjain

sreyansjain

You can have inputs with a form attribute. The input doesn’t need to inside the form.

sreyansjain

sreyansjain

I too have a similar requirement, I am going with the approach of one live component per cell. I am trying to create something like airtable in liveview. So my cell datatype is dynamic.

ryanzidago

ryanzidago OP

You can have inputs with a form attribute. The input doesn’t need to inside the form.

I have tried this, this is approach 2) but could not make it work with LiveView.

ryanzidago

ryanzidago OP

Unfortunately, I don’t know enough about spreadsheets or the problem space to answer you this question :sweat_smile:

My educated guess tells me that we care much more about rows than columns as each rows are isolated from one another. There will be no dependencies in the like of:

If row 1 has this value, then row 2 should have that value.

ryanzidago

ryanzidago OP

@BartOtten @sreyansjain

In the end, I’ve decided to ditch streams and resort to one live component per row. This works well for now and no focus is lost on input changes.

This post and the fact that they come with the default generator made me think that streams should be the go to for handling a large collections in LiveViev.

Though after dealing with the spreadsheet use case + reading this comment from @garrison it seems that I’m missing some nuance on when and when not to use streams.

cmo

cmo

I would go with option 6. The others feel like a lot of work.

garrison

garrison

I’m glad to see we’ve reached the point where I don’t even need to reply to these threads anymore :slight_smile: But yes, you are the latest in a long line of victims here.

Thankfully the introduction of keyed :for has substantially improved the situation and the docs now recommend those by default which I am of course very happy about. I don’t know which generators use Streams but those should definitely be changed IMO.

Note that thanks to keyed :for you no longer need to use the LiveComponent hack to get good diffs, though in your particular case (a large grid) I would probably prefer the additional control.

There is very little nuance left in my position at this point; I think Streams should be deprecated. Others (including the Phoenix team) probably disagree with me, though!

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
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
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
csmr
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley: Mutonex gamedesign was finish...
New

Other Trending Topics Top

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
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
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews