nallwhy
What is the best way to update a form from outside the form in LiveView?
Sometimes I need to update a form from outside the form in LiveView.
- For UI, some inputs are need to be created outside the form.
- Updating form when file is uploaded.
I made an simple example.
The hour input is outside the form.
When hour input is changed, select_hour event is triggered.
page: Json Media · Json Media
code: https://github.com/nallwhy/json_corp/blob/main/apps/json_corp_web/lib/json_corp_web/live/playgrounds/form_live.ex
Marked As Solved
dfalling
To answer your question, I’ve handled this in two different ways in my application:
- do a
Changeset.put_change(changeset, :time, time)in my event handler, so the data gets persisted into my form state. I have a hidden input like you do to ensure the value persists when other changes happen to the form.
@impl true
def handle_event("select_hour", %{"hour" => hour_str}, socket) do
time = Time.new!(hour_str |> String.to_integer(), 0, 0)
changeset =
socket.assigns.form.source
|> Ecto.Changeset.put_change(:time, time})
{:noreply, assign_form(socket, changeset)}
end
- if I’m in a live component that I want to be more reusable (not requiring the parent to implement a
handle_event), I call aassign(socket, :value, hour), and use a small hook I wrote that will trigger the formchangeevent so Phoenix’s standard form handling will detect it and populate it.
But as your current example is, I think both of these are overkill. I would instead improve the select’s data so it already has the times as values. Then there’s no necessary conversion step and you can put the time select in the same form as the name.
defp time_options() do
0..23
|> Enum.map(fn hour ->
label = "#{hour}:00"
value = Time.new!(hour, 0, 0)
[key: label, value: value]
end)
end
This returns this data which you can use in your select:
[
[key: "0:00", value: ~T[00:00:00]],
[key: "1:00", value: ~T[01:00:00]],
...
[key: "23:00", value: ~T[23:00:00]]
]
Note: I haven’t tested this- I’ve render %Time{} into input and it works fine. Assuming it also works as a value for select.
Also Liked
nallwhy
Thank you!
I solved it with params of Changeset following your first suggestion, because put_change/3 doesn’t validate the changes.
@impl true
def handle_event("select_hour", %{"hour" => hour_str}, socket) do
time = Time.new!(hour_str |> String.to_integer(), 0, 0)
form =
socket.assigns.form.source.params
|> Map.put("time", time)
|> Routine.changeset()
|> Map.put(:action, :validate)
|> to_form()
{:noreply, assign(socket, :form, form)}
end
TwistingTwists
in a similar approach, I have a helper function that takes params, and returns updated params.
Because, I have 3 external fields. (uploads, timers etc) outside the form
becomes
form =
socket.assigns.form.source.params
|> add_timer(timer)
|> add_uploads()
|> Routine.changeset()
|> Map.put(:action, :validate)
|> to_form()
defp timer(params, timer), do: Map.put(params,"timer", timer)
defp add_uploads(), do: ....
I like your proposed solution.
dfalling
Sure, here it is:
// Hook that will dispatch change event when it's updated. Useful for inputs that are
// updated by a component so they won't fire a change event.
Hooks.PublishInput = {
updated() {
this.el.dispatchEvent(new Event("input", { bubbles: true }));
},
};
Note: this triggers the input event, which by itself won’t get back to LiveView. It has to be inside a managed form which will then trigger whatever change handling you have.
Last Post!
dfalling
Sure, here it is:
// Hook that will dispatch change event when it's updated. Useful for inputs that are
// updated by a component so they won't fire a change event.
Hooks.PublishInput = {
updated() {
this.el.dispatchEvent(new Event("input", { bubbles: true }));
},
};
Note: this triggers the input event, which by itself won’t get back to LiveView. It has to be inside a managed form which will then trigger whatever change handling you have.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









