URL state synchronization for bookmarkable table views.
Syncs table state (filters, sort, page, search) to URL query params, allowing users to bookmark or share specific table views.
Configuration
Enable URL sync in domain table config:
mishka_gervaz do
table do
url_sync do
enabled true
params [:filters, :sort, :page]
prefix "table"
end
end
endUsage in LiveView
In your parent LiveView, handle URL params:
def handle_params(params, _uri, socket) do
url_state = MishkaGervaz.Table.Web.UrlSync.decode(params, "table")
{:noreply,
socket
|> assign(:url_state, url_state)}
endPass to the component:
<.live_component
module={MishkaGervaz.Table.Web.Live}
id="posts-table"
resource={MyApp.Post}
url_state={@url_state}
/>Param Format
- Filters:
?table_filter_status=active&table_filter_category=1 - Sort:
?table_sort=name:ascor?table_sort=inserted_at:desc - Page:
?table_page=2 - Search:
?table_search=hello - Template:
?table_template=grid
See MishkaGervaz.Table.Web.State,
MishkaGervaz.Table.Web.State.UrlSync (the in-state sub-builder),
MishkaGervaz.Table.Dsl.UrlSync (resource-level DSL section),
MishkaGervaz.Table.Dsl.Defaults (domain-level defaults).
Summary
Functions
Apply URL state to existing table state.
Build URL path with encoded state params.
decode/3 with the resource's own url_sync configuration overridden.
Check if URL sync is enabled in config.
Encode table state to URL query params.
Check if url_state matches current table state.
Whether url_state was decoded from url: the same path and the same query params, in any
order.
Types
@type decode_opts() :: [ allowed_params: [atom()], allowed_filters: [atom()], max_filter_length: non_neg_integer() ]
Functions
Apply URL state to existing table state.
Merges decoded URL state with default table state.
Build URL path with encoded state params.
Takes current path and state, returns path with query string.
@spec decode(map(), String.t(), module(), decode_opts()) :: url_state() | nil
decode/3 with the resource's own url_sync configuration overridden.
Every option defaults to what the resource declared, so pass only what differs:
:allowed_params— which of:filters:sort:page:page_size:search:templateto read:allowed_filters— the filter names that may arrive from the URL:max_filter_length— values longer than this are ignored:prefix— the param prefix
Returns nil when the resource has URL sync disabled, exactly as decode/3 does.
UrlSync.decode(params, uri, MyApp.Blog.Post, allowed_filters: [:status])
Check if URL sync is enabled in config.
Encode table state to URL query params.
Examples
iex> state = %{filters: %{status: "active"}, sort: [{:name, :asc}], page: 2}
iex> UrlSync.encode(state, %{params: [:filters, :sort, :page], prefix: "t"})
%{"t_filter_status" => "active", "t_sort" => "name:asc", "t_page" => "2"}
Check if url_state matches current table state.
Used to detect if incoming url_state is from our own push_patch (to avoid duplicate data reload).
Whether url_state was decoded from url: the same path and the same query params, in any
order.
UrlSync.same_url?(UrlSync.decode(params, "/posts?sort=name:asc", MyApp.Post), "/posts?sort=name%3Aasc")
#=> trueA url_state decoded without a URI has no :query, and is never the same.