Behaviour implemented by all item actions.
Summary
Callbacks
The base item / schema to use for the changeset. The result will be passed as the first parameter to changeset/3 each time it is called.
cancel button label
The changeset to be used in the item action. It is used to validate form inputs.
This text is being displayed in the confirm dialog.
Confirm button label
A list of fields to be displayed in the item action. See Backpex.Field. In addition you have to provide
a type for each field in order to support changeset generation.
Performs the action. It takes the socket, the list of affected items, and the casted and validated data (received from Ecto.Changeset.apply_action/2).
Action icon
Action label (Show label on hover)
Returns a URL path for the item action. When implemented, the action renders as a <.link navigate={path}>
instead of a <button>. This enables standard browser link behavior such as Ctrl+click to open in a new tab
and right-click context menus.
Functions
Defines Backpex.ItemAction behaviour and provides default implementations.
Prepares the socket for opening an action confirmation modal.
Re-reads items from the data layer and authorizes the reloaded records against key.
Returns default item actions.
Calls handle/3 with assigns.item_action_key set to key, and clears the assign again on the
socket the action returns.
Handles an item action by executing the action's handle function.
Checks whether item action has confirmation modal.
Checks whether item action has form.
Checks whether item action has a link callback.
Resolves a client-supplied item action key and decides how the gesture proceeds.
Callbacks
@callback base_schema(assigns :: map()) :: Ecto.Schema.t() | Ecto.Changeset.t() | {Ecto.Changeset.data(), Ecto.Changeset.types()}
The base item / schema to use for the changeset. The result will be passed as the first parameter to changeset/3 each time it is called.
This function is optional and can be used to use changesets with schemas in item actions. If this function is not provided,
a schemaless changeset will be created with the provided types from fields/0.
cancel button label
@callback changeset( change :: Ecto.Schema.t() | Ecto.Changeset.t() | {Ecto.Changeset.data(), Ecto.Changeset.types()}, attrs :: map(), metadata :: keyword() ) :: Ecto.Changeset.t()
The changeset to be used in the item action. It is used to validate form inputs.
Additional metadata is passed as a keyword list via the metadata parameter.
The list of metadata:
:assigns- the assigns:target- the name of theformtarget that triggered the changeset call. Defaults tonilif the call was not triggered by a form field.
This text is being displayed in the confirm dialog.
There won't be any confirmation when this function is not defined.
Confirm button label
@callback fields() :: list()
A list of fields to be displayed in the item action. See Backpex.Field. In addition you have to provide
a type for each field in order to support changeset generation.
The following fields are currently not supported:
@callback handle( socket :: Phoenix.LiveView.Socket.t(), items :: [map()], params :: map() | struct() ) :: {:ok, Phoenix.LiveView.Socket.t()} | {:error, Ecto.Changeset.t()}
Performs the action. It takes the socket, the list of affected items, and the casted and validated data (received from Ecto.Changeset.apply_action/2).
Exactly one of handle/3 or link/2 must be implemented for each item action.
If link/2 is implemented, handle/3 must not be defined, and vice versa. Link-based actions navigate directly without a server round-trip.
You must return either {:ok, socket} or {:error, changeset}.
If {:ok, socket} is returned, the action is considered successful by Backpex and the action modal is closed. However, you can add an error flash message to the socket to indicate that something has gone wrong.
If {:error, changeset} is returned, the changeset is used to update the form to display the errors. Note that Backpex already validates the form for you.
Therefore it is only necessary in rare cases to perform additional validation and return a changeset from handle/3.
For example, if you are building a duplicate action and can only check for a unique constraint when inserting the duplicate element.
You are only allowed to return {:error, changeset} if the action has a form. Otherwise Backpex will throw an ArgumentError.
@callback icon(assigns :: map(), item :: struct()) :: %Phoenix.LiveView.Rendered{ caller: term(), dynamic: term(), fingerprint: term(), root: term(), static: term() }
Action icon
Action label (Show label on hover)
Returns a URL path for the item action. When implemented, the action renders as a <.link navigate={path}>
instead of a <button>. This enables standard browser link behavior such as Ctrl+click to open in a new tab
and right-click context menus.
Functions
Defines Backpex.ItemAction behaviour and provides default implementations.
Prepares the socket for opening an action confirmation modal.
If the action has a form, it creates a changeset and assigns it along with the base schema. Otherwise, it assigns an empty changeset.
Re-reads items from the data layer and authorizes the reloaded records against key.
Returns the reloaded items, in the order they were given, ready to be handed to dispatch/5.
This is the authoritative execution gate for item actions, and both execution paths go through
it: handle_item_action/5 for an action that runs immediately, and the confirmation modal's
submit in Backpex.FormComponent.
It reloads first because a selection is a snapshot. Rows are cached in assigns.selected_items
when they are selected, and a confirmation modal can stay open for as long as the user likes.
Authorizing the snapshot would let a record that has changed since then — a user promoted to
admin, say — be mutated under a permission it no longer has, because the write that follows
addresses the row by its primary key. The records handed to handle/3 are therefore the ones
that were just authorized, not the ones that were rendered.
Failure semantics are those of Backpex.Authorization.authorize_all!/4: an item that no longer
exists, or that has left the LiveResource's item_query/3 scope, reloads as nil and raises
Backpex.NoResultsError; a reloaded item that is not authorized raises Backpex.ForbiddenError.
Nothing is silently dropped from the selection.
The resource fields used for the reload are derived from assigns.live_action, so associations
are preloaded exactly as they are for the view the action was triggered from.
The reload is not a lock
A window remains between the reload and whatever handle/3 writes. Backpex deliberately does
not open a transaction or lock the rows here: handle/3 owns the write and decides what
isolation it needs. An action that requires strict atomicity has to re-read and lock inside its
own handle/3 — for example in an Ecto.Repo.transaction/2 with a lock: "FOR UPDATE"
query.
Returns default item actions.
Calls handle/3 with assigns.item_action_key set to key, and clears the assign again on the
socket the action returns.
The key is scoped to exactly one dispatch. Leaving it set would let a later dispatch — or any component rendered afterwards — read the key of an action that already finished.
Returns whatever handle/3 returned. Reloading and authorizing are the caller's job: this only
runs the action. Pass it the list authorize_fresh!/3 returned, never a cached selection.
Handles an item action by executing the action's handle function.
The selection is reloaded and authorized against key through authorize_fresh!/3 before
handle/3 runs, and it is the reloaded records that handle/3 receives — see there for
what that guarantees and what it does not. This is strict: a single unauthorized item raises
Backpex.ForbiddenError, and an item that no longer exists (or a stale or forged item id) raises
Backpex.NoResultsError. Items are never silently dropped from the selection.
handle/3 receives the full list of items, and assigns.item_action_key is available for the
duration of that call only — see dispatch/5. assigns.selected_items is updated to the same
reloaded records, so an action that reads it sees what it was handed.
Because this gate covered exactly these items under exactly this key, a Backpex.Resource call
inside handle/3 that writes those same items should pass authorize?: false rather than
repeat the check. Writes to other items or resources keep the default gate.
When the selection is empty, handle/3 is not called at all — only after_handle runs.
Checks whether item action has confirmation modal.
Checks whether item action has form.
Checks whether item action has a link callback.
Resolves a client-supplied item action key and decides how the gesture proceeds.
Returns {:confirm, key, action} for an action that has a confirmation modal and
{:dispatch, key, action} for one that runs immediately. An unregistered key raises
Backpex.NoResultsError.
Only the {:confirm, _key, _action} path is authorized here, before the modal opens: an
unauthorized selection must not even get a confirm dialog. This is a preflight check on the
items as they are currently rendered — it only decides whether the dialog opens. The modal's
submit runs authorize_fresh!/3, which reloads the records first and is the authoritative gate,
so nothing is lost by not reloading here.
The {:dispatch, _key, _action} path is deliberately not authorized here — handle_item_action/5
gates it, and that is the authoritative execution gate. Checking in both places would evaluate
Backpex.LiveResource.can?/3 twice per item for one decision.