JohnnyCurran

JohnnyCurran

The Top 3 LiveView Form Mistakes (And How to Fix Them)

I’ve been writing LiveView since 2020. In that time, I’ve seen the same three form mistakes at multiple companies. Here’s what they are and how to fix them.

1. Slow, laggy forms with scattered logic because form state gets stored in socket assigns and server round-trips get used for dynamic UI (conditional inputs, toggles), instead of keeping that state in hidden form inputs where it belongs.

2. Brittle system where UI and database can’t evolve independently because database schemas get used directly for forms, coupling persistence logic to presentation.

3. Users stuck with valid data but can’t submit because changesets get manually manipulated with Map.put or Map.merge instead of Ecto.Changeset functions, leaving stale errors behind.

The common thread: don’t fight the framework. Keep form state on the client, create embedded schemas for your forms, and use Ecto.Changeset functions to modify changesets.

Most Liked

kevinschweikert

kevinschweikert

In Mistake #2, where you do:

 {:error, changeset} ->
      {:noreply, assign(socket, :form, to_form(changeset))}

How would you map the errors back to the form schema, when the fields from the DB are different?

tfwright

tfwright

I don’t have much professional experience with LiveView, just hobby projects. But RE 1, aren’t things a bit more complex than you suggest? You say “don’t fight the framework,” but naively it would seem using JS at all is fighting a framework the express intention of which is presumably to manage state on the server. Your example is a good one for your argument, seems like pure FE state that is relatively easy to handle (with latest JS integration). But in reality isn’t most state more ambiguous?

Take a table with rows that are selectable. On first glance this seems like something that should be handled on the FE. Certainly requiring a server trip to toggle selection creates lag, and arguably breaks a strong user expectation that checkboxes are highly responsive. But what happens when some BE action needs to know which rows are selected? Or even needs to rerender part of the view (e.g. to display some metadata about selected rows)? Obviously, you can solve this with more JS, but now all the defects you mentioned with a BE implementation apply: the logic is more spread out, more place for bugs, and arguably JS bugs are a lot more difficult to test and QA against (at least, that’s why we’re using LV in the first place, no?). Alternatively, one could try to use different tooling to alleviate issues with lag, like debouncing, optimistic UI updates, etc.

To be clear, I don’t think your advice is necessarily bad, in the end I think it is simply one of the challenges of development with LV to find the right balance here. But it is a balance, and a delicate one. The more JS features get added to a LV project the lower the ROI it seems. At a certain point, if you want to add a lot of these features, DX is going to go downhill in comparison with React.

JohnnyCurran

JohnnyCurran

Good question.

In that specific case branch, nothing needs to be done, because that changeset is the form changeset.

If there were an error in Accounts.register_user and you got a changeset back, you’d do something like (psuedo-ish code):

changeset = FormModule.changeset(%FormModule{}, form_params)
changeset
|> Ecto.Changeset.apply_action(:insert)
|> case do
  {:ok, form_params} ->
    form_params
    |> Map.from_struct()
    |> Accounts.register_user()
    |> case do
      {:error, user_changeset} ->
         # Figure out which user changeset field had an error
         # Place error in changeset, validate, re-assign
         socket =
           changeset
           |> Ecto.Changeset.put_error(:field, "There was an error saving to the database!")
           |> Map.put(:action, :validate)
           |> to_form()
           |> then(&assign(socket, :form, &1))
         
        # ... rest of handler

Is how I’ve done it before

Where Next?

Popular in Blog Posts Top

tmartin8080
Sharing some articles I’ve written recently while working in Elixir. Some are how-to guides and others for reference. Will update this ...
New
brainlid
Building a LiveView powered chat app is easier than ever when using Streams! Sophie DeBenedetto shows us how in this article. She createa...
New
AstonJ
Elixir Blog Posts How to use this section You can post links to your blog posts either in one of the Official Blog Posts threads, or, vi...
New
RudManusachi
Hi there! Recently I was playing around with extracting and updating data in the DB and for fun challenged myself to try to implement a ...
New
New
alvises
Just published the first part of series of articles aimed to explain the architecture behind a kv-store engine written in Elixir and impl...
New
zacksiri
In December of 2023 we came to the realization that we needed to build our own image server. After hitting a few snags we decided that it...
New
ragamuf
Does the world need another How to create a blog article? Maybe not. But then again, creating something out of nothing is what we love....
New
sheharyarn
In this article, I talk about the Elixir library Delta we at Slab just open-sourced, its various features including support for Operation...
New
paulanthonywilson
Whatever your Nerves project does, there’s a good chance that it can be enhanced by securely connecting to a Phoenix Server in the cloud;...
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31013 112
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36352 110
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement