Phoenix LiveView how to edit content in place?

I am trying to have the server update the database whenever the div on the client is changed. I think the code I have tried does not work because phx-change only works on forms.

I have read through Phoenix.LiveView.JS, and was thinking of using key events events while monitoring whether or not the content of the div has changed. However, that seems overly complex, and I may be overlooking some capabilities of Phoenix and LiveView.

Where else should look to figure out how to accomplish this?

def handle_event("update", _, socket) do
   # update database with new content
    {:noreply, socket}
  end

def render(assigns) do
  ~H"""
  <div contenteditable="true" phx-change="update">
    word
  </td>
  """
end

If you are doing rich text editing it is better to consider solutions such as CKEditor. If you only need plain text you can dress your text area up with CSS. Proper editing and two way synchronisation of contenteditable is very difficult. Hope this helps.

2 Likes

CKEditor looks like it would work if I was creating a professional product. But I am doing this as a personal project to learn how to implement something like that.

andyl/phoenix_live_editable: In-place editing for Phoenix Live View (github.com) is an attempt in this direction. Hope it helps for learning purposes.
Ofcourse, livebook-dev/livebook: Interactive and collaborative code notebooks for Elixir - built with Phoenix LiveView (github.com) has great content editing in place. But, it has too much other functionality, and separating content editable part might be difficult.

1 Like

phoenix_live_editable looks like the perfect place to learn how to get started on this

Taking your requirements at face value, you could create a JS Hook where on mounted you hook up Mutation Observer to your dom and send events to the server as needed.

1 Like

livebook uses a form for their in-place editing. I have adapted it slightly for my use-case, which looks something like:

              <form phx-change="update">
                <input
                  type="text"
                  id={"list-" <> Integer.to_string(list.id)}
                  name="title"
                  value={list.title}
                  spellcheck="false"
                  autocomplete="off"
                />
                <input type="hidden" name="id" value={list.id} />
              </form>

Careful! Input elements with name="id" in live view forms can cause some very annoying bugs, because that causes the .id attribute of the form element to hold the DOM element of the input with that name rather than id attribute value of the form. See for instance this thread. The solution is to use a name other than id for your input—in this case, perhaps it could be name="list-id".

2 Likes
                <form phx-change="update">
                <input
                  type="text"
                  id={"list-" <> Integer.to_string(list.id)}
                  name="title"
                  value={list.title}
                  spellcheck="false"
                  autocomplete="off"
                />
                <input type="hidden" name="list-id" value={list.id} />
              </form>