Form phx-change don't work after csv download

Hi guys!
I try CSV Export with Phoenix and LiveView with this guide CSV Export with Phoenix and LiveView - Tutorials and screencasts for Elixir, Phoenix and LiveView (fullstackphoenix.com).
I use filters for data displayed as a table on my heex:

<form phx-change=“filter” class=“pt-4 pb-4 text-sm”>
<span class=“inline-flex”>
<span>
<label>node:</label>
<%= Phoenix.HTML.Form.number_input(:options, :node,
value: @options.node,
“phx-debounce”: “300”,
class: “text-sm border border-slate-300 text-slate-600 w-20 ml-2 mr-4”
) %>
</span>
<span>
<label>date:
<%= Phoenix.HTML.Form.date_input(:options, :date,
value: @options.date,
class: “text-sm border border-slate-300 text-slate-600 ml-2 mr-4”
) %>
</span>
</span>
</form>

and link to export on the same heex:

<.link
href={~p"/export?#{%{message: “journals”, node: @options.node, date: @options.date, sort_by: @options.sort_by, sort_order: @options.sort_order}}“}
method=“post”
class=”-ml-px inline-flex items-center px-3 h-10 border border-slate-300 bg-white text-base font-medium text-slate-600 no-underline hover:bg-slate-300 rounded-md"
>
Export
</.link>

In export controller:

def create(conn, %{“message” => “journals”, “node” => node, “date” => date, “sort_by” => sort_by, “sort_order” => sort_order}) do

csv_data = …

conn
|> put_resp_content_type(“text/csv”)
|> put_resp_header(“content-disposition”, “attachment; filename="journals.csv"”)
|> put_root_layout(false)
|> send_resp(200, csv_data)
end

CSV export works fine, but i have some quiestion: the filter (form) stops responding to changes after loading csv-file, but all other links work (pagination and sorting).
It seems that “phx-change” don’t send any messages to my live_view.

I also read this topics, but could not find an answer to my question:

Thank you.

Off the top of my head, I suspect what’s going on here is that the LiveView socket disconnects on export since clicking a default generated <.link href=...> results in traditional browser navigation. You can check if this is the case by using the phx-disconnected binding to show/do something.

If that is the case, you could try specifying the anchor tag’s download attribute and/or target attribute as new tab and see if that makes a difference.

Updated to add:

Thank you codeanpeace!
target=“_blank” works fine, but this causes the opening of a blank page for a short time. It seems not very nice.

I couldn’t get download attribute to work. I try this:

<.link
href={~p"/export?#{%{message: “journals”, node: @options.node, date: @options.date, sort_by: @options.sort_by, sort_order: @options.sort_order}}“}
method=“post”
download
…>
Export
</.link>

or

<.link
href={~p"/export?#{%{message: “journals”, node: @options.node, date: @options.date, sort_by: @options.sort_by, sort_order: @options.sort_order}}“}
method=“post”
download = “journals.csv”
…>
Export
</.link>

Do you have any thoughts on this?

Hmm, could you elaborate a bit more? Are you seeing an error? Does no download take place? What does the rendered html of the a anchor tag look like?

The MDN docs list linked above mentions some requirements for the download attribute for anchor elements and different browsers may handle things a bit differently as well.

As I can see, there are no errors.
Dev-tools console output:

phx-F46Bx0s-9TzePR4i socket: disconnect for page nav - undefined
phx-F46Bx0s-9TzePR4i destroyed: the child has been removed from the parent - undefined
destroyed: the child has been removed from the parent - undefined

Yes, it works well.

HTML code for:

<.link
href={~p"/export?#{%{message: “journals”, node: @options.node, date: @options.date, sort_by: @options.sort_by, sort_order: @options.sort_order}}“}
method=“post”
download
…>
Export
</.link>

looks like this:

<a href=“/export?message=journals&sort_by=date&sort_order=desc&node=3&date=” data-method=“post” data-csrf=“DHpdChIqN3AaPQUjBSdfCSIaWRknNnF64IeXjx_IlUABDkfXmJtNsP6-” data-to=“/export?message=journals&sort_by=date&sort_order=desc&node=3&date=” class=“-ml-px inline-flex items-center px-3 h-10 border border-slate-300 bg-white text-base font-medium text-slate-600 no-underline hover:bg-slate-300 rounded-md” download=“”>
Export
</a>

I try it in two browsers: edge and chrome, the behavior in both cases is the same.

P.S. Just in case:

mix phx.new --version
Phoenix installer v1.7.2

elixir --version
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.15.4 (compiled with Erlang/OTP 26)

That’s odd, LiveView shouldn’t disconnect when the download attribute as of this PR: Fix for #2552: do not unload LiveView socket on `download` link clicks. by sherbondy · Pull Request #2611 · phoenixframework/phoenix_live_view · GitHub

I’d suggest taking a few steps back and directly specifying the anchor tag with the download attribute temporarily just to see if that changes anything and get it working as expected.

<a href="/export?message=journals&sort_by=date&sort_order=desc&node=3&date=" download>
Export
</a>

I try this:

<a
href=“/export?message=journals&sort_by=date&date=&sort_order=desc&node=”
data-method=“post”
data-to=“/export?message=journals&sort_by=date&date=&sort_order=desc&node=”
data-csrf=“xxx”
class=“-ml-px inline-flex items-center px-3 h-10 border border-slate-300 bg-white text-base font-medium text-slate-600 no-underline hover:bg-slate-300 rounded-md”
download
>
Export
</a>

The behavior is the same:

phx-F47RLs3Fj_DdkRFB socket: disconnect for page nav - undefined
phx-F47RLs3Fj_DdkRFB destroyed: the child has been removed from the parent - undefined
destroyed: the child has been removed from the parent - undefined

Perhaps some other settings are required somewhere else?

That’s odd, LiveView shouldn’t disconnect when the download attribute as of this PR: Fix for #2552: do not unload LiveView socket on download link clicks. by sherbondy · Pull Request #2611 · phoenixframework/phoenix_live_view · GitHub

I find this code in phoenix_live_view/assets/js/phoenix_live_view/dom.js:

wantsNewTab(e){
let wantsNewTab = e.ctrlKey || e.shiftKey || e.metaKey || (e.button && e.button === 1)
let isDownload = (e.target instanceof HTMLAnchorElement && e.target.hasAttribute(“download”))
let isTargetBlank = e.target.hasAttribute(“target”) && e.target.getAttribute(“target”).toLowerCase() === “_blank”
return wantsNewTab || isTargetBlank || isDownload
},

I additionally tried simple GET (not POST):

<a
href=“/export?message=journals&sort_by=date&date=&sort_order=desc&node=”
download
>
Export
</a>

and got the same result: download file works well, but “socket: disconnect for page nav” and “destroyed: the child has been removed from the parent”.

wantsNewTab(e){
let wantsNewTab = e.ctrlKey || e.shiftKey || e.metaKey || (e.button && e.button === 1)
let isDownload = (e.target instanceof HTMLAnchorElement && e.target.hasAttribute(“download”))
let isTargetBlank = e.target.hasAttribute(“target”) && e.target.getAttribute(“target”).toLowerCase() === “_blank”
return wantsNewTab || isTargetBlank || isDownload
},

BTW (for FF and chrome):
e.ctrlKey - don’t work in my case
e.shiftKey - works, but open a blank page
download - don’t work in my case
target=“_blank” - works, but open a blank page for a short time