LiveView and Rolling Restarts

Duping what I posted on the phoenix issue for posterity:

Form recovery is supported with js hooks, but is not yet automatic. Automatic recovery is on the roadmap, but we will have to have the client check to ensure all its stashed inputs match the latest rendered state from the server. So in some cases for multi-step or heavily dynamic forms, auto recovery won’t work, but for the basic cases we can have the client automatically recover. In the meantime and for advanced forms, you can annotate your form with phx-hook="SavedForm", then define a JS hook which stashes the form state and passes it back up via connect params, for example:

# <%= f = form_for @changeset, "#", phx_hook: "SavedForm", phx_change: :validate, phx_submit: :save %>

def mount(_session, socket) do
  changeset =
    case get_connect_params(socket) do
      %{"stashed_form" => encoded} ->
        %User{}
        |> Accounts.change_user(Plug.Conn.Query.decode(encoded)["user"])
        |> Map.put(:action, :insert)

      _ ->
        Accounts.change_user(%User{})
    end

  {:ok, assign(socket, changeset: changeset)}
end
let serializeForm = (form) => {
  let formData = new FormData(form)
  let params = new URLSearchParams()
  for(let [key, val] of formData.entries()){ params.append(key, val) }

  return params.toString()
}

let Params = {
  data: {},
  set(namespace, key, val){
    if(!this.data[namespace]){ this.data[namespace] = {}}
    this.data[namespace][key] = val
  },
  get(namespace){ return this.data[namespace] || {} }
}

Hooks.SavedForm = {
  mounted(){
    this.el.addEventListener("input", e => {
      Params.set(this.viewName, "stashed_form", serializeForm(this.el))
    })
  }
}

let socket = new LiveSocket("/live", {hooks: Hooks, params: (view) => Params.get(view)})
socket.connect()

Note that the above url encodes the form, which requires decoding it on the server. You could write more JS to serialize the form as json to avoid this step :slight_smile:

11 Likes