Bump Your Deps
Update Backpex to the latest version:
defp deps do
[
{:backpex, "~> 0.22.0"}
]
endv0.22 lets LiveView re-render the index and show views only where something changed. The table, the filters, the action buttons and the fields of the show view no longer re-render on every change of the LiveView once you tell Backpex which assigns your callbacks need. This is opt-in: without configuration, your callbacks receive the same assigns as before. Only saving inline edits changes without configuration, see Inline edits are saved by the index view.
If you use Phoenix LiveView 1.1 or later, never call Backpex components from your
own render_resource_slot/3 or LiveViews, have no custom index editable fields,
your layout assigns the values it derives with assign/3 and neither
on_item_updated/2 nor an index_editable function reads other assigns of the
field component than its field, item and value, there is nothing to do.
Otherwise, read on.
1. Phoenix LiveView 1.1 is required
Backpex now requires {:phoenix_live_view, "~> 1.1"}. It already needed the
LiveView compiler of 1.1 before, and the index table now uses the :key
attribute, which LiveView 1.0 does not support. Update LiveView first if your
application is still on 1.0:
- {:phoenix_live_view, "~> 1.0"},
+ {:phoenix_live_view, "~> 1.1"},2. Optional: configure context_assigns
Callbacks that run while the index and show views render, like
Backpex.LiveResource.can?/3, Backpex.LiveResource.index_row_class/4, item
actions, filters and fields, receive the assigns of the LiveView. LiveView cannot
know which of them they read, so by default Backpex re-renders these parts on
every change of the LiveView.
List the assigns your callbacks need in the new :context_assigns option:
use Backpex.LiveResource,
...,
context_assigns: [:current_scope]These callbacks then only receive the listed assigns and the ones Backpex always
passes (:live_resource, :live_action, :params, :fields, :item_actions,
:return_to, the :item of the show view, which is nil on the index view or
the base_schema of a resource action while one is open, and a :socket for
building routes). The functions and callbacks of fields on the index view
receive the item of their row as :item instead.
Warning
A callback that reads an assign you did not list gets a KeyError or nil.
Add every assign that your can?/3, index_row_class/4, item actions, filters
and fields read, and check the index and show views of the LiveResource after
the change. The :socket only carries the router and endpoint.
The default, context_assigns: :all, keeps passing every assign. See the
Context Assigns guide for details.
3. Inline edits are saved by the index view
The index view now saves inline edits of index_editable fields itself, with
all of its assigns like the edit form, and the edited item as :item and the
name of the field as :name, like the field component. The
changeset and can?/3 of an inline edit receive these assigns whatever you set
:context_assigns to. Before, the field component saved the edit with its own
assigns.
This also applies to the built-in fields and changes what two callbacks receive:
on_item_updated/2receives the socket of the LiveView instead of the socket of the field component. Its:itemassign isnilon the index view, so use theitemargument instead ofsocket.assigns.item.- An
index_editablefunction is called again before the edit is saved, with the assigns of the LiveView and, like the field component, the:field, its:field_options, its:name, the current:itemand its:value. Other assigns of the field component, like the ones returned byBackpex.Field.index_assigns/3, are not available there.
The index view reads the item again before it saves the edit, so the checks
and the changeset see its current values. It checks that the field is index
editable and not readonly, that it implements
Backpex.Field.index_editable_change/3, that the item is on the current page
and that can?/3 allows :edit for it. It also saves no inline edit while a
resource action is open, as the adapter would use the changeset of the resource
action. An edit that is not saved keeps its value and stays marked as invalid
until the item is saved or reloaded. The index view logs a warning for the index
form of a field that does not implement index_editable_change/3.
The index form of Backpex.Fields.Boolean now disables the toggle of a readonly
field, as browsers ignore readonly on checkboxes.
The index form of Backpex.Fields.BelongsTo only saves an option of its select,
i.e. a record of its options_query, or no option.
The index forms of the built-in fields now send an "index-edit" event to the
LiveView. If you render Backpex.HTML.Resource.resource_index_table/1 or
Backpex.HTML.Resource.resource_index_main/1 with index editable fields in a
LiveView of your own, that LiveView receives this event instead of the field
component. Handle it there, as LiveView raises for events without a
handle_event/3 clause. The params contain the name of the field in
"index_edit" => %{"field" => ...}, the primary value of the item in
"index_edit" => %{"item" => ...} and the value in
"index_form" => %{"value" => ...}:
def handle_event("index-edit", %{"index_edit" => %{"field" => field, "item" => item_id}} = params, socket) do
value = get_in(params, ["index_form", "value"])
# Check that the user may edit the field of the item and save the value.
{:noreply, socket}
endThe built-in fields no longer handle an "update-field" event themselves.
Custom index editable fields keep working: an index form that sends its own
event to the field component and calls Backpex.Field.handle_index_editable/3
still saves the edit, but with the assigns of the field component. With a list
of :context_assigns, these assigns only include the listed ones.
Backpex.Field.handle_index_editable/3 now also refuses to save a field that is
not index editable or readonly, an item that can?/3 does not allow to :edit,
or while a resource action is open, so an event sent to the field component can
no longer change such a field. It then keeps the value in the form and assigns
@valid as false. The index view does not save the edits of such a
field, so an "index-edit" event cannot skip the checks of its own event
handler.
To let the index view save the edits of a custom field, render its form with
Backpex.HTML.Form.index_form/1, assign it with
Backpex.Field.assign_index_form/1 and implement the new
Backpex.Field.index_editable_change/3 callback, which returns the change to
save:
@impl Backpex.Field
def render_index_form(assigns) do
- form = to_form(%{"value" => assigns.value}, as: :index_form)
-
- assigns =
- assigns
- |> assign_new(:form, fn -> form end)
- |> assign_new(:valid, fn -> true end)
+ assigns = Backpex.Field.assign_index_form(assigns)
~H"""
<div>
- <.form
- for={@form}
- id={"index-form-#{@name}-#{LiveResource.primary_value(@item, @live_resource)}"}
- phx-change="update-field"
- phx-submit="update-field"
- phx-target={@myself}
- >
+ <Backpex.HTML.Form.index_form form={@form} name={@name} item={@item} live_resource={@live_resource}>
<Backpex.HTML.Form.input type="text" field={@form[:value]} ... />
- </.form>
+ </Backpex.HTML.Form.index_form>
</div>
"""
end
- @impl Phoenix.LiveComponent
- def handle_event("update-field", %{"index_form" => %{"value" => value}}, socket) do
- Backpex.Field.handle_index_editable(socket, value, Map.put(%{}, socket.assigns.name, value))
- end
+ @impl Backpex.Field
+ def index_editable_change({name, _field_options}, value, _assigns), do: %{name => value}Build the change in index_editable_change/3 as your handle_event/3 did, e.g.
save the value to another key than the name of the field or normalize it. See
the Index Edit
guide.
4. Backpex components in your own slots
The index and show views are now composed of components that receive their
assigns as attributes, so LiveView can track each of them. Components you call
with {assigns} in your own render_resource_slot/3 keep working without
changes, and their callbacks receive the assigns you pass them.
If you call Backpex components with explicit attributes instead, check these changes:
- Backpex now declares every attribute these components read. A call that misses
a required attribute, e.g.
item_countofBackpex.HTML.Resource.resource_index_main/1, now gets a compile warning. Such calls already failed when rendering before. Backpex.HTML.Resource.resource_filters/1no longer computes the active filters itself. Pass them with the newfiltersattribute, e.g.filters={@filters}. Without it, no filters are shown. The index view evaluates@filterswith all of its assigns on every render, as before.- Pass
index_edits={@index_edits}toBackpex.HTML.Resource.resource_index_main/1andBackpex.HTML.Resource.resource_index_table/1, so an inline edit that cannot be saved is marked as invalid. - Pass
backpex_context={@backpex_view_context}to use the assigns of the:context_assignsoption in the callbacks of the component. Without it, they receive the assigns you pass the component.
5. Layouts are change tracked
Backpex now renders your layout with the change tracking of the LiveView, like
any other function component. Before, it re-rendered the whole layout on every
change. A value your layout derives from its assigns without assign/3, e.g.
with Map.put/3, is no longer updated. Use assign/3 instead:
def admin(assigns) do
- assigns = Map.put(assigns, :section, section(assigns.current_url))
+ assigns = assign(assigns, :section, section(assigns.current_url))Checklist
- [ ] Phoenix LiveView is updated to 1.1 or later.
- [ ] If you set
:context_assignsto a list, it includes every assign your`can?/3`, `index_row_class/4`, item actions, filters and fields read. - [ ] Custom index editable fields render
Backpex.HTML.Form.index_form/1andimplement `index_editable_change/3`, or you are fine with them saving with the assigns of the field component. - [ ] Custom fields that save with
Backpex.Field.handle_index_editable/3areindex editable, not readonly and `can?/3` allows `:edit` for the item. - [ ]
on_item_updated/2implementations do not rely on the socket of the fieldcomponent, e.g. on `socket.assigns.item`. - [ ]
index_editablefunctions only read the:field,:field_options,`:name`, `:item` and `:value` assigns of the field component. - [ ] Backpex components called with explicit attributes in your own
`render_resource_slot/3` get all required attributes, `filters` for `resource_filters/1` and `index_edits` for the index table. - [ ] LiveViews of your own that render the index table with index editable
fields handle the `"index-edit"` event. - [ ] Your layout assigns the values it derives with
assign/3.