# Upgrading to v0.22

## Bump Your Deps

Update Backpex to the latest version:

```elixir
defp deps do
  [
    {:backpex, "~> 0.22.0"}
  ]
end
```

v0.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](#3-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:

```diff
- {: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
`c:Backpex.LiveResource.can?/3`, `c: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:

```elixir
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 {: .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](../live_resource/context-assigns.md) 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/2` receives the socket of the LiveView instead of the socket of
  the field component. Its `:item` assign is `nil` on the index view, so use the
  `item` argument instead of `socket.assigns.item`.
- An `index_editable` function 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 `:item` and its `:value`. Other
  assigns of the field component, like the ones returned by
  `c:Backpex.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
`c: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" => ...}`:

```elixir
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}
end
```

The 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
`c:Backpex.Field.index_editable_change/3` callback, which returns the change to
save:

```diff
  @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](../fields/index-edit.md#custom-index-editable-implementation)
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_count` of `Backpex.HTML.Resource.resource_index_main/1`,
  now gets a compile warning. Such calls already failed when rendering before.
- `Backpex.HTML.Resource.resource_filters/1` no longer computes the active
  filters itself. Pass them with the new `filters` attribute, e.g.
  `filters={@filters}`. Without it, no filters are shown. The index view
  evaluates `@filters` with all of its assigns on every render, as before.
- Pass `index_edits={@index_edits}` to `Backpex.HTML.Resource.resource_index_main/1`
  and `Backpex.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_assigns` option 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:

```diff
  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_assigns` to 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/1` and
      implement `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/3` are
      index editable, not readonly and `can?/3` allows `:edit` for the item.
- [ ] `on_item_updated/2` implementations do not rely on the socket of the field
      component, e.g. on `socket.assigns.item`.
- [ ] `index_editable` functions 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`.
