Shared push_patch navigation for the admin DataTable SPA filter contract.
The DataTable LiveComponent renders the filter <form> with phx-change/
phx-submit="data_table_filter" (parent-targeted — no phx-target). Each list
LiveView delegates that event here, which merges the submitted filter params
into the table's canonical path and push_patches the result.
Why merge instead of append
socket.assigns.table_path already carries any pre-existing query string. In
organization mode it is "/billing/customers?org=acme" (from each page's
scoped_path/3). A naive path <> "?" <> URI.encode_query(params) would emit
"/billing/customers?org=acme?q=foo" — a corrupting second ? that drops the
org param and breaks owner-scoping on every apply/change/clear.
patch_with_filters/3 instead decodes the existing query, merges the filter
params over it (filter params win for keys they set), drops blank/nil values so
cleared fields disappear from the URL, and re-encodes a SINGLE query string. The
pre-existing org (and any other) query param survives every apply/change/clear.
Summary
Functions
Builds the merged path string without touching a socket.
Merges params into path's existing query and push_patches the socket.
Functions
Builds the merged path string without touching a socket.
Exposed for unit testing the merge/no-double-?/blank-drop/org-only behavior.
@spec patch_with_filters(Phoenix.LiveView.Socket.t(), String.t(), map()) :: Phoenix.LiveView.Socket.t()
Merges params into path's existing query and push_patches the socket.
- decodes
path's existing query string (e.g.org=acme) - merges
paramsover it (filter params win) - drops
nil/""values so cleared fields leave the URL - re-encodes a single query string (no double
?) push_patches to the resulting path (no trailing?when empty)