Proper way to update Phoenix.HTML.Form after a `put_change` on the changset

I am building a LiveView form where, after initial user input, validation of the changeset includes “normalizing” one of the fields (let’s call it :address). The form struct is being generated by Phoenix.Component.to_form/2, with a changeset as the first input.

I had thought that by calling Ecto.Changeset.put_change on the changeset with the normalized :address, the form would be immediately updated with this normalized address:

# ...inside my "validate" handle_event callback
socket =
  assign(
    socket,
    :the_form,
    socket.assigns.the_form.data
    |> Businesses.change_business(params)
    |> put_change(:address, "New normalized address")
    |> to_form(as: "business", action: :validate)

{:noreply, socket}

However, the form still displays the non-normalized value for :address. I can get the form input to finally show the normalized :address only if another "validate" event gets triggered (perhaps by updating a different field.)

I noticed that the Phoenix.HTML.Form returned by to_form has the correct .source.changes, but its :params are still the old params. I tried (against my better judgement) manually updating these :params so that they reflect the normalized address, but even this didn’t cause the form the re-render.

I must be missing something here about the happy-path for getting this form input to update with the normalized address. Can someone please point me in the right direction?

I think you may be running into this:

More here:

@sodapopcan Thank you for the pointer! FWIW I had some trouble even doing it with a hook (LiveView really does not want to touch that input!), but by using a new id I was able to get a decent solution.

I am curious though, is this not a common need (normalizing a user input and showing them the normalized value in the form)? Wondering what other people might have done in this situation.

It’s certainly a common need, but not one you want to subject to the latency to a server – at least when changing it beneigh users feet. Even JS libraries running on the client often mess this stuff up and there’s no network involved.

Generally I’d suggest going with different approaches. E.g. stripe/google maps address normalization just shows a dropdown to suggest their normalized address format instead of updating the input the user is currently typing in.

2 Likes