Push Event on upload runs but immediately reverts

Anyone have any idea why a push_event would seemingly work, but then the changes the handler makes to the page are immediately rescinded?

My app is handling a file upload and when the upload occurs I send a push event to the client which simply hides one div and makes another visible. The upload works and the event handler runs – I see the div disappear and the other div become visible for a split second, but then it reverts as if nothing had changed.

This is my upload code:

defp handle_upload(:file, entry, socket) do
    if entry.done? do
      upload = consume_uploaded_entry(socket, entry, fn meta -> {:ok, %{entry: entry, meta: meta}} end)
      json  = upload.meta.data
      fname = upload.entry.client_name
      case Jason.decode(json) do
        {:ok, data} ->
          case Quote.create(data) do
            {:ok, record} ->
              socket = socket
              |> assign(:file, fname)
              |> put_flash(:info, "File #{fname} uploaded.")
              |> push_event("view-report", %{})   # HERE IS THE PUSH!!!
              {:ok, record, socket} |> handle_response()
            {:error, changeset} ->
              {:error, changeset, socket} |> handle_response()
          end
        {:error, reason} ->
          {:noreply, socket |> put_flash(:error, reason) }
      end
    else
      {:noreply, socket}
    end
  end

Don’t mind the handle_response calls, they just incorporate the changeset into the socket return the proper tuple, e.g. {:noreply, socket}

Here is my setup for the uploader in mount:

    { :ok,
      socket
      |> assign(data)
      |> allow_upload(:file, accept: ~w(.json), auto_upload: true, progress: &handle_upload/3,
           writer: fn _name, _entry, _socket -> {CustomUploader, level: :debug} end)
    }

The event handler on the page is simple enough:

window.addEventListener("phx:view-report", function (event) {
    console.log(event);
    e1 = document.getElementById("editform");
    e2 = document.getElementById("report");
    e1.style.display = "none";
    e2.style.display = "block";
  });

You can’t just manipulate the DOM like that through events since it knows nothing about change tracking. The server is simply replacing your div with what it thinks it should be. In order to manipulate elements in this way you need to phx-update="ignore" them. However, this wouldn’t be a good idea in this case since you want your form change-tracked. You should use JS Commands which do know about change tracking. I think you want JS.dispatch instead of the push_event, it’s been a bit since I’ve tried that, someone else hopefully will know better. Since there is a request being made anyway, I generally keep this kind of state on the server in LiveView state, though that is by no means “better” or anything! I just find it a little simpler.

2 Likes

Maybe have a look at JS.show and JS.hide, that’s what is being used in core_components.

2 Likes

@sodapopcan I thought it must be something like that, just wasn’t sure how to get around it. Your idea of just adding some state is a clever idea. I see the appeal as it is a very straightforward approach. However, I took this as an opportunity to get more familiar with the JS stuff, something I’ve been putting off and only used once before in a copy-paste way. Reading about it made more sense now, so very helpful. Thanks.

@Hermanverschooten Yep, that’s the ticket. Appears to be working great. Thanks.

1 Like