3
votes

I have a Phoenix LiveView with a form that is not backed by a data layer, like so:

<%= f = form_for :post, "#", [phx_submit: :create_post %>
  <%= textarea f, :message, placeholder: "Say something:" %>
  <%= hidden_input f, :user_id, value: @current_user.account_id %>
  <%= submit "Post" %>
</form>

I can't back the form with a changeset because I am not using Ecto. After submitting the form, the submission is processed just fine, but the form textarea is not cleared. How can I clear inputs without resorting to Javascript?

If I can't do it without Javascript, how can I do it with Javascript, but without bypassing the LiveView phx-submit mechanisms?

Some additional troubleshooting information:

Here is my event handler:

def handle_event("create_post", %{"post" => post_params}, socket) do
  thread_id = socket.assigns.thread.id
  user_id = post_params["user_id"]
  posts = Forums.append_post!(thread_id, user_id, post_params)
  UdsWeb.Endpoint.broadcast_from(self(), build_topic(thread_id), "new_post", %{posts: posts})
  {:noreply, assign(socket, :posts, posts)}
end

I've tried several different approaches to fix the problem, mostly involving variations of data structures backing the form.

  • I've tried backing the form with a map. This doesn't work because forms must be backed with structures that implement the Phoenix.HTML.FormData protocol, and Phoenix only implements this for Plug.Conn and Atom
  • I've tried using a struct, but this doesn't work for the same reason as maps
  • I don't have a Conn to use in my form, because this is a LiveView, but I could create a Conn in the LiveView controller, so I did. I backed the form with it and passed a new instance through in the event handler for post creation. This did not solve the problem.
  • Finally, I changed the textarea to a text_input, and this input cleared immediately on submission of the form. So it seems the problem is specific to the textarea element. I'm not sure whether or not this is a bug with Phoenix.
2
I believe in your response handler you should explicitly create the new Post object.Aleksei Matiushkin
I believe there are no objects.Wojciech Bednarski

2 Answers

2
votes

As Aleksei said in his comment: You have to pass a new Post struct from your controller to your view. For example like this:

def handle_event("create_post", post, socket) do
    # Here do what you want with the data from the "post" parameter

    {:noreply, assign(socket, :post, %Post{})}   
end
0
votes

Even it behaves like a SPA it is not a SPA, so you still need to use the backend router and redirect it back to the index page. Your form is on the index page but the resource is not the post's index page, it is post/new.

So, you need to use push_redirect (not redirect):

|> push_redirect(to: UdsWeb.post_index_path(socket, :index))
def handle_event("create_post", %{"post" => post_params}, socket) do
  thread_id = socket.assigns.thread.id
  user_id = post_params["user_id"]
  posts = Forums.append_post!(thread_id, user_id, post_params)
  UdsWeb.Endpoint.broadcast_from(self(), build_topic(thread_id), "new_post", %{posts: posts})
  {:noreply,
   socket
   |> push_redirect(to: UdsWeb.post_index_path(socket, :index))}
end

Hex: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#push_redirect/2