Single state struct for MishkaGervaz table.
All table state is held in this struct rather than in separate assigns.
State shape
State is split into two parts:
static- Configuration that never changes (same reference for O(1) comparison)- Dynamic fields - User interaction state that triggers re-renders
This separation allows LiveView to skip re-rendering static parts (columns, filters, etc.) when only dynamic state (page, filter_values, etc.) changes.
The state holds no rows. A load's rows reach the page through the table's stream, and
records_result is the Phoenix.LiveView.AsyncResult of the last load: %{page: page, empty?: boolean} of the load that started the list once it succeeds, which Load more and Load
earlier keep, failed with the error once a load fails. Only a table with
keep_loaded_records true keeps its loaded rows, in loaded_records. after_keyset is the keyset
of the last row a load read, the one string Load more reads after when the read action allows
keyset pagination. On a read action without keyset, Load more on a table that keeps no rows
reads from next_offset instead: the end of the rows the view shows, one less for each row taken
out of the list. removed_ids holds the ids of the rows the view took out itself until their own
realtime removal arrives, so each is counted once.
hidden_ids holds the picked rows, of selected_ids, that the table's read no longer finds, such
as rows a search hides. A bulk action reaches only the rows the read finds, so the bulk bar counts
the picked rows without them and says how many the filters hide. A load that starts the list
afresh while rows are picked finds them in one read of their ids.
page is the last page the view shows, the one the URL holds. first_page is the first: a
:load_more or :infinite table opened at page N, as a reload after Load more opens it, shows
page N alone, and Load earlier reads the page before it, one page a click, after the keyset of
the first row shown, before_keyset. A table that keeps its rows reads from the first page, so
its first_page is 1.
Sub-builders
State initialization is composed of sub-builders that can be overridden:
ColumnBuilder- Builds columns from DSL and resourceFilterBuilder- Builds filters from DSL and resourceActionBuilder- Builds row/bulk actionsPresentation- Resolves UI adapter and templatesUrlSync- Handles URL state synchronizationAccess- Handles access control
User Override
Override the entire state module:
defmodule MyApp.Table.State do
use MishkaGervaz.Table.Web.State
def init(id, resource, user) do
state = super(id, resource, user)
%{state | custom_field: :value}
end
endOverride specific sub-builders:
defmodule MyApp.Table.State do
use MishkaGervaz.Table.Web.State,
column: MyApp.Table.ColumnBuilder,
filter: MyApp.Table.FilterBuilder
endOr override via DSL:
mishka_gervaz do
table do
state do
column MyApp.Table.ColumnBuilder
filter MyApp.Table.FilterBuilder
end
end
endOverride entire state module via DSL:
mishka_gervaz do
table do
state module: MyApp.Table.CustomState
end
endHelper Functions
Helper functions are available in MishkaGervaz.Table.Web.State.Helpers and can be
used when overriding state functions:
defmodule MyApp.Table.State do
use MishkaGervaz.Table.Web.State
alias MishkaGervaz.Table.Web.State.Helpers, as: StateHelpers
def hydrate_relation_filter_labels(state) do
# Use helpers in your override
StateHelpers.hydrate_filter(filter, acc, state)
end
endSee MishkaGervaz.Table.Web.State.Helpers,
MishkaGervaz.Table.Web.State.ColumnBuilder,
MishkaGervaz.Table.Web.State.FilterBuilder,
MishkaGervaz.Table.Web.State.ActionBuilder,
MishkaGervaz.Table.Web.State.Presentation,
MishkaGervaz.Table.Web.State.UrlSync,
MishkaGervaz.Table.Web.State.Access,
MishkaGervaz.Table.Web.AutoState,
MishkaGervaz.Table.Web.Refresh,
MishkaGervaz.Table.Web.UrlSync.
Summary
Functions
Applies a mount's own presentation choices over the resource's — see the __using__ version.
See MishkaGervaz.Table.Web.State.Default.apply_url_state/2.
See MishkaGervaz.Table.Web.State.Default.bidirectional_url_sync?/1.
See MishkaGervaz.Table.Web.State.Default.can_modify_record?/2.
See MishkaGervaz.Table.Web.State.Default.default_init/3.
See MishkaGervaz.Table.Web.State.Default.get_action/2.
See MishkaGervaz.Table.Web.State.Default.get_preloads/1.
Hydrate relation filter state with labels for selected values from URL
See MishkaGervaz.Table.Web.State.Default.init/3.
See MishkaGervaz.Table.Web.State.Default.record_visible?/2.
See MishkaGervaz.Table.Web.State.Default.switch_template/2.
See MishkaGervaz.Table.Web.State.Default.template_switching_enabled?/1.
See MishkaGervaz.Table.Web.State.Default.update/2.
Types
@type archive_status() :: :active | :archived
@type loading_status() :: :initial | :loading | :loaded | :error
@type loading_type() :: :initial | :reset | :more | :earlier
@type t() :: %MishkaGervaz.Table.Web.State{ after_keyset: String.t() | nil, archive_status: archive_status(), base_path: String.t() | nil, before_keyset: String.t() | nil, current_page_size: pos_integer() | nil, current_user: map() | nil, dismissed_notices: MapSet.t(), excluded_ids: MapSet.t(any()), expanded_data: struct() | nil, expanded_id: String.t() | nil, filter_values: map(), first_page: pos_integer(), has_initial_data?: boolean(), has_more?: boolean(), hidden_ids: MapSet.t(any()), loaded_records: list(), loading: loading_status(), loading_type: loading_type(), master_user?: boolean(), next_offset: non_neg_integer(), page: integer(), path_params: map(), preload_aliases: %{required(atom()) => atom()}, preserved_params: map(), records_result: Phoenix.LiveView.AsyncResult.t(), relation_filter_state: %{required(atom()) => map()}, removed_ids: MapSet.t(), saved_active_state: map() | nil, saved_archived_state: map() | nil, select_all?: boolean(), selected_ids: MapSet.t(any()), sort_fields: [{atom(), :asc | :desc}], static: MishkaGervaz.Table.Web.State.Static.t(), supports_archive: boolean(), template: module(), total_count: integer() | nil, total_pages: integer() | nil }
Functions
Applies a mount's own presentation choices over the resource's — see the __using__ version.
See MishkaGervaz.Table.Web.State.Default.apply_url_state/2.
See MishkaGervaz.Table.Web.State.Default.bidirectional_url_sync?/1.
See MishkaGervaz.Table.Web.State.Default.can_modify_record?/2.
See MishkaGervaz.Table.Web.State.Default.default_init/3.
See MishkaGervaz.Table.Web.State.Default.get_action/2.
See MishkaGervaz.Table.Web.State.Default.get_preloads/1.
Hydrate relation filter state with labels for selected values from URL
See MishkaGervaz.Table.Web.State.Default.init/3.
See MishkaGervaz.Table.Web.State.Default.record_visible?/2.
See MishkaGervaz.Table.Web.State.Default.switch_template/2.
See MishkaGervaz.Table.Web.State.Default.template_switching_enabled?/1.
See MishkaGervaz.Table.Web.State.Default.update/2.