Bump Your Deps
Update Backpex to the latest version:
defp deps do
[
{:backpex, "~> 0.23.0"}
]
endNULL values are ordered by the database default
Before v0.23, Backpex ordered every column with ASC NULLS FIRST or DESC NULLS LAST. A regular index (including the
primary key) can't serve these orders, so the database sorted the whole table on every page load of the index view,
even for the default order by primary key. On large tables this is slow and writes temporary files, especially on later
pages.
Backpex now uses plain ASC and DESC and leaves NULL values to the database. In PostgreSQL, NULL values now come
last in ascending and first in descending order. Columns without NULL values order the same as before.
To keep the old order, set the new order_nulls option to :smallest, on the LiveResource or on a single field:
use Backpex.LiveResource,
# ...other options
order_nulls: :smallestpublished_at: %{
module: Backpex.Fields.DateTime,
label: "Published at",
order_nulls: :smallest
}order_nulls also accepts :first and :last for NULL values in a fixed place in both directions. Every value except
:default needs a matching index on large tables (for :smallest:
create index(:posts, ["published_at NULLS FIRST"])). See NULL Values in
the ordering guide.
Ordering by the primary_key always uses plain ASC and DESC.
If you have a custom adapter, the order criteria passed to list/4 now has a :nulls key (:default, :first,
:last or :smallest).
Texts for every upload error
Before v0.23, the form of an upload field crashed with a FunctionClauseError on any upload error other than
:too_large, :too_many_files and :not_accepted, for example when the browser's upload to S3 failed
(:external_client_failure) or an external function returned {:error, meta, socket}. Backpex now shows a text for
every error. If you translate Backpex's texts, add translations for the new upload texts in backpex.pot.
A custom upload field can give its own texts with the new optional callback Backpex.Field.upload_error/4. It's
called on the field's module, so it also works for a field that delegates render_form/1 to Backpex.Fields.Upload.
It gets every error, so delegate the ones your field doesn't handle to Backpex.Fields.Upload.upload_error/4:
@impl Backpex.Field
def upload_error(_field, :too_large, upload, _assigns) do
Backpex.translate_error({"must be at most %{count} bytes", count: upload.max_file_size})
end
def upload_error(field, error, upload, assigns) do
Backpex.Fields.Upload.upload_error(field, error, upload, assigns)
endFields without the callback keep the texts of Backpex.Fields.Upload, so nothing changes for them.
Backpex.HTML.Resource.pagination/1 takes a page_path function
Before v0.23, the index view decoded the whole path for the pagination links so that Backpex.HTML.Resource.pagination/1
could replace a :page placeholder in it. Decoding also undid the encoding of the search and filter values, so page
links broke values containing &, #, + or %. For example, page 2 of the search R&D searched for R.
The path attribute is replaced by page_path, a function that receives a page number and returns the path to that
page. If you render the component yourself, change:
<.pagination path={"/admin/posts?page=:page"} current_page={@page} total_pages={@total_pages} />to:
<.pagination page_path={&"/admin/posts?page=#{&1}"} current_page={@page} total_pages={@total_pages} />Build the path with an encoding function like Backpex.Router.get_path/5 or Plug.Conn.Query.encode/1 if it contains
user input.
InlineCRUD deletes rows with a button
The per-row delete control of Backpex.Fields.InlineCRUD is now a button[name="change[<field>_delete][]"] that can be
used with the keyboard. The hidden checkbox with the id <field>-checkbox-delete-<index> is gone, so update custom CSS,
JavaScript or tests that target it. If you translate Backpex's texts, add a translation for Delete entry %{index}.
InlineCRUD child field classes apply to table cells
The rows of Backpex.Fields.InlineCRUD now share one CSS table, so the inputs and delete buttons of all rows line up.
The class option of a child field lands on the table cell of that column, in the header row and in every entry row.
Width classes like w-1/3 keep working. Flex classes like grow, basis-1/4 or shrink-0 have no effect on a table
cell anymore, and display classes like hidden md:block override table-cell and break the row. Columns without a
class no longer get flex-1; they share the remaining width.
Primary keys are encoded once in paths
Before v0.23, Backpex encoded the primary key in show links twice and turned a space into +, and the show and edit
views decoded the key a second time. String primary keys with a space or a literal %XX sequence didn't find their
item. Backpex now encodes every path param exactly once as a path segment (a space becomes %20, a slash %2F) and
uses the value Phoenix decoded. Path params of scopes (for example scope "/:tenant") are encoded the same way.
This changes generated URLs: old bookmarks of double-encoded show links (for example /posts/a%252Fb/show for the key
a/b) no longer resolve.
If your own code works around the old encoding, remove the workaround:
- Don't call
URI.decode/1onparams["backpex_id"]; it's already decoded. - Pass the plain value to
Backpex.Router.get_path/5andBackpex.Router.get_path/6; don't encode it first.
"Save & Continue editing" on the new view now builds the edit path with Backpex.Router.get_path/5, so the LiveResource
needs an edit route for it.
Unused text "Unselect %{label}"
The remove icon in a selected MultiSelect badge no longer has an accessible name, so Backpex no longer uses the text
Unselect %{label}. If you translate Backpex's texts, you can remove it from your translations.
Empty date and time values skip the format function
Before v0.23, a format function on a Backpex.Fields.Date, Backpex.Fields.DateTime or Backpex.Fields.Time field
was also called with nil, so the index and show views crashed for every item with an empty value unless the function
handled nil. Backpex now renders empty values as "—" without calling the function, as it does for format strings.
If your function returned a text of its own for nil (for example fn nil -> "never"; date -> ... end), that text
is now replaced by "—". To keep it, move the formatting to the field's render option:
deleted_at: %{
module: Backpex.Fields.DateTime,
label: "Deleted At",
render: fn assigns ->
~H"""
<p>{if @value, do: Calendar.strftime(@value, "%d.%m.%Y"), else: "never"}</p>
"""
end
}render replaces the field's whole value template in the index and show views: format no longer applies, and the
truncate class the default template adds in the index view is only there if you add it. It isn't used for
index-editable fields (index_editable), which render their input instead.
Upload fields need at least one entry
Backpex.Fields.Upload accepted max_entries: 0, but every new and edit form with such a field crashed with a
BadMapError. The max_entries option now has to be a positive integer. With 0, the views of the LiveResource raise
a configuration error for the field. Fields of a resource action aren't validated against the config schema, so there
0 raises the ArgumentError of Phoenix.LiveView.allow_upload/3 when the action's form opens.
To show a field's files without letting users upload or remove files, set readonly: true on the field instead (see
the readonly guide).