Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fdae5e1
Use filtered fields in ecto adapter
krns Jul 2, 2025
21eb844
fix filter options docs
krns Jul 2, 2025
18c2d07
refactoring
krns Jul 2, 2025
352c1d5
wip
krns Jul 2, 2025
d20030f
wip
krns Jul 2, 2025
539dc45
format
krns Jul 2, 2025
22e2e8f
wip
krns Jul 2, 2025
5dff8a4
wip
krns Jul 2, 2025
840ce5e
wip
krns Jul 2, 2025
8b26604
wip
krns Jul 2, 2025
0612e9c
wip
krns Jul 2, 2025
6d8566c
wip
krns Jul 2, 2025
8f32d2f
wip
krns Jul 2, 2025
f022456
wip
krns Jul 2, 2025
ff7516b
wip
krns Jul 2, 2025
3cb583a
wip
krns Jul 2, 2025
460d69e
wip
krns Jul 2, 2025
a896377
Remove fields assign
krns Jul 3, 2025
9d0a16f
fixes
krns Jul 3, 2025
a331076
fixes
krns Jul 3, 2025
ddd64ef
fix
krns Jul 3, 2025
03edca5
fix HasManyThrough fields
krns Jul 3, 2025
a256b3c
assign fields for rendering
krns Jul 3, 2025
188dd32
format
krns Jul 3, 2025
f06c514
fix typo
krns Jul 3, 2025
2f22ac3
revert
krns Jul 3, 2025
10c0aed
wip
krns Jul 3, 2025
939dc36
align Resource.update/6
krns Jul 3, 2025
9cda78b
fix docs
krns Jul 3, 2025
80308c4
fix docs
krns Jul 3, 2025
a91be70
fix tests
krns Jul 3, 2025
ef26bdd
disable doctests
krns Jul 3, 2025
ef9650b
fix bug id record_query
krns Jul 15, 2025
a9b711e
pass fields instead of live_action
krns Jul 15, 2025
ce55b7a
Merge branch 'develop' into feature/use-filtered-fields-in-ecto-adapter
krns Jul 15, 2025
392d227
fix warning
krns Jul 15, 2025
c69350d
add fields parameter to ash adapter
krns Jul 15, 2025
1266b51
assign fields in form component
krns Jul 15, 2025
413edcf
use fields from assigns in html related code
krns Jul 16, 2025
2c94fd8
remove fields param
pehbehbeh Jul 17, 2025
e96b836
fix resource action fields
pehbehbeh Jul 17, 2025
6ff128c
improve upgrade guide
pehbehbeh Jul 17, 2025
aa65fb3
Merge branch 'develop' into feature/use-filtered-fields-in-ecto-adapter
krns Jul 18, 2025
c718d2c
fix docs
krns Jul 18, 2025
dfccfaf
simplify fields only/except option to always use :index in case of :r…
pehbehbeh Jul 22, 2025
1a7182b
align docs
krns Jul 22, 2025
d1f2d23
fix? HasManyThrough field
krns Jul 22, 2025
3c43f1f
format
krns Jul 22, 2025
03b5fc6
Merge branch 'develop' into feature/use-filtered-fields-in-ecto-adapter
krns Jul 25, 2025
6d33273
Update guides/upgrading/v0.14.md
Flo0807 Sep 5, 2025
2708c40
Update guides/upgrading/v0.7.md
Flo0807 Sep 5, 2025
5117402
Update lib/backpex/live_resource/index.ex
Flo0807 Sep 5, 2025
0a915ad
Merge branch 'develop' into feature/use-filtered-fields-in-ecto-adapter
Flo0807 Sep 5, 2025
377214d
Update upgrade guide
Flo0807 Sep 5, 2025
43a2fb6
Use map type instead of list for get adapter function
Flo0807 Sep 5, 2025
0158677
Remove `:resource_action` from except lists
Flo0807 Sep 5, 2025
3420fc8
Read fields from assigns
Flo0807 Sep 5, 2025
7a700b7
Add`@impl` to `return_to`
Flo0807 Sep 5, 2025
154b1d2
Add `resource_action` as available live action
Flo0807 Sep 5, 2025
519362b
Remove unused variable
Flo0807 Sep 5, 2025
929132d
Merge branch 'develop' into feature/use-filtered-fields-in-ecto-adapter
Flo0807 Sep 5, 2025
17deb33
Pass lists explicitly to adapter and resource module
Flo0807 Sep 5, 2025
d3276f9
Update order of function arguments
Flo0807 Sep 5, 2025
5c39fc8
Remove resource_action pattern in has_many_through
Flo0807 Sep 5, 2025
f01abab
Update upgrade guide
Flo0807 Sep 5, 2025
735757b
Fix fields were passed as wrong param
Flo0807 Sep 5, 2025
0447cdd
Fix missing param
Flo0807 Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions demo/lib/demo_web/live/post_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ defmodule DemoWeb.PostLive do
module: Backpex.Fields.Textarea,
label: "Body",
rows: 10,
except: [:index, :resource_action],
except: [:index],
align_label: :center
},
published: %{
Expand All @@ -123,7 +123,7 @@ defmodule DemoWeb.PostLive do
module: Backpex.Fields.Boolean,
label: "Show likes",
select: dynamic([post: p], fragment("? > 0", p.likes)),
except: [:index, :resource_action, :show]
except: [:index, :show]
},
likes: %{
module: Backpex.Fields.Number,
Expand Down
2 changes: 1 addition & 1 deletion demo/lib/demo_web/live/product_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ defmodule DemoWeb.ProductLive do
assigns ->
~H"<p>{Backpex.HTML.pretty_value(@value)}</p>"
end,
except: [:index, :resource_action]
except: [:index]
},
name: %{
module: Backpex.Fields.Text,
Expand Down
1 change: 1 addition & 0 deletions demo/lib/demo_web/live/short_link_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ defmodule DemoWeb.ShortLinkLive do
def can?(_assigns, :delete, _item), do: false
def can?(_assigns, _action, _item), do: true

@impl Backpex.LiveResource
def return_to(_socket, _assigns, :edit, _form_action, _item) do
# since the primary key might be updated, we go to the index page
~p"/admin/short-links"
Expand Down
2 changes: 1 addition & 1 deletion guides/fields/visibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ You can change the visibility of fields in certain views.

You can use the `only` and `except` options to define the views where a field should be visible. The `only` option will show the field only in the specified views, while the `except` option will show the field in all views except the specified ones. The options have to be a list of view names.

The following values are supported: `:new`, `:edit`, `:show`, `:index` and `:resource_action`.
The following values are supported: `:new`, `:edit`, `:show` and `:index`.

```elixir
# in your resource configuration file
Expand Down
2 changes: 1 addition & 1 deletion guides/filter/how-to-add-a-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule MyAppWeb.Filters.PostCategorySelect do
def prompt, do: "Select category ..."

@impl Backpex.Filters.Select
def options do
def options(_assigns) do
query =
from p in Post,
join: c in Category,
Expand Down
2 changes: 1 addition & 1 deletion guides/live_resource/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ When working with forms (in `:new` or `:edit` live actions), the following form
- `:save` - When a form is successfully submitted aka the "Save" button was clicked
- `:cancel` - When a form submission is canceled aka the "Cancel" button was clicked

For all other live actions, the form_action will be `nil`.
For all other live actions, the form_action will be `nil`.
31 changes: 31 additions & 0 deletions guides/upgrading/v0.15.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,34 @@ Note that it is now also possible to configure the layout as a function:
use Backpex.LiveResource,
layout: &MyAppWeb.admin/1
```

## Resource and adapter functions have been updated

We've updated some functions in `Backpex.Resource` and the adapter modules (`Backpex.Adapters.Ecto` and `Backpex.Adapters.Ash`) to include the fields as an additional parameter.

The following functions are affected:

`Backpex.Resource`:
- `list/3` -> `Backpex.Resource.list/4`
- `count/3` -> `Backpex.Resource.count/4`
- `get/3` -> `Backpex.Resource.get/4`
- `get!/3` -> `Backpex.Resource.get!/4`

`Backpex.Adapter` (including `Backpex.Adapters.Ecto` and `Backpex.Adapters.Ash`):
- `list/3` -> `c:Backpex.Adapter.list/4`
- `count/3` -> `c:Backpex.Adapter.count/4`
- `get/3` -> `c:Backpex.Adapter.get/4`

For example:

```elixir
# before
Resource.get(primary_value, socket.assigns, live_resource)
# after
Resource.get(primary_value, fields, socket.assigns, live_resource)
```

## `:only`/`:except` field option changes

You no longer need to pass `:resource_action` in addition to `:index` to the fields `:only`/`:except` option.
Before, it was needed to make fields visible behind the backdrop of the resource action modal.
10 changes: 5 additions & 5 deletions guides/upgrading/v0.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ end
We have updated certain functions in `Backpex.Resource`.

The following functions are affected:
- `Backpex.Resource.update/6` (`update/5` before)
- [`Backpex.Resource.insert/6`]() (`insert/5` before)
- [`Backpex.Resource.change/7`]()
- [`Backpex.Resource.put_assocs/2`]() (has been removed)
- `update/6` (`update/5` before)
- `insert/6` (`insert/5` before)
- `change/7`
- `put_assocs/2` (has been removed)

If you call one of these functions in your application, you will probably need to update the function call.

Expand Down Expand Up @@ -85,4 +85,4 @@ def label(_assigns, _item) do
end
```

Read more about the new `item` parameter in [the item action guide](/guides/actions/item-actions.md#implementing-an-item-action).
Read more about the new `item` parameter in [the item action guide](/guides/actions/item-actions.md#implementing-an-item-action).
26 changes: 18 additions & 8 deletions lib/backpex/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,51 @@ defmodule Backpex.Adapter do

Should return `nil` if no result was found.
"""
@callback get(term(), map(), module()) :: {:ok, struct() | nil} | {:error, term()}
@callback get(primary_value :: term(), fields :: list(), assigns :: map(), live_resource :: module()) ::
{:ok, struct() | nil} | {:error, term()}

@doc """
Returns a list of items by given criteria.
"""
@callback list(keyword(), map(), module()) :: {:ok, list()}
@callback list(criteria :: keyword(), fields :: list(), assigns :: map(), live_resource :: module()) :: {:ok, list()}

@doc """
Gets the total count of the current live_resource.
Possibly being constrained the item query and the search- and filter options.
"""
@callback count(keyword(), map(), module()) :: {:ok, non_neg_integer()}
@callback count(criteria :: keyword(), fields :: list(), assigns :: map(), live_resource :: module()) ::
{:ok, non_neg_integer()}

@doc """
Inserts given item.
"""
@callback insert(struct(), module()) :: {:ok, struct()} | {:error, term()}
@callback insert(item :: struct(), live_resource :: module()) :: {:ok, struct()} | {:error, term()}

@doc """
Updates given item.
"""
@callback update(struct(), module()) :: {:ok, struct()} | {:error, term()}
@callback update(item :: struct(), live_resource :: module()) :: {:ok, struct()} | {:error, term()}

@doc """
Updates given items.
"""
@callback update_all(list(struct()), keyword(), module()) :: {:ok, non_neg_integer()}
@callback update_all(items :: list(struct()), updates :: keyword(), live_resource :: module()) ::
{:ok, non_neg_integer()}

@doc """
Applies a change to a given item.
"""
@callback change(struct(), map(), term(), list(), module(), keyword()) :: Ecto.Changeset.t()
@callback change(
item :: struct(),
attrs :: map(),
fields :: term(),
assigns :: list(),
live_resource :: module(),
opts :: keyword()
) :: Ecto.Changeset.t()

@doc """
Deletes multiple items.
"""
@callback delete_all(list(struct()), module()) :: {:ok, term()} | {:error, term()}
@callback delete_all(items :: list(struct()), live_resource :: module()) :: {:ok, term()} | {:error, term()}
end
6 changes: 3 additions & 3 deletions lib/backpex/adapters/ash.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if Code.ensure_loaded?(Ash) do
Returns `nil` if no result was found.
"""
@impl Backpex.Adapter
def get(primary_value, _assigns, live_resource) do
def get(primary_value, _fields, _assigns, live_resource) do
resource = live_resource.adapter_config(:resource)
primary_key = live_resource.config(:primary_key)

Expand All @@ -42,7 +42,7 @@ if Code.ensure_loaded?(Ash) do
Returns a list of items by given criteria.
"""
@impl Backpex.Adapter
def list(_criteria, _assigns, live_resource) do
def list(_criteria, _fields, _assigns, live_resource) do
live_resource.adapter_config(:resource)
|> Ash.read()
end
Expand All @@ -51,7 +51,7 @@ if Code.ensure_loaded?(Ash) do
Returns the number of items matching the given criteria.
"""
@impl Backpex.Adapter
def count(_criteria, _assigns, live_resource) do
def count(_criteria, _fields, _assigns, live_resource) do
live_resource.adapter_config(:resource)
|> Ash.count()
end
Expand Down
18 changes: 8 additions & 10 deletions lib/backpex/adapters/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ defmodule Backpex.Adapters.Ecto do
Gets a database record with the given primary key value.
"""
@impl Backpex.Adapter
def get(primary_value, assigns, live_resource) do
def get(primary_value, fields, assigns, live_resource) do
repo = live_resource.adapter_config(:repo)

record_query(primary_value, assigns, live_resource)
record_query(primary_value, assigns, fields, live_resource)
|> repo.one()
|> then(fn result -> {:ok, result} end)
end
Expand All @@ -87,10 +87,10 @@ defmodule Backpex.Adapters.Ecto do
Returns a list of items by given criteria.
"""
@impl Backpex.Adapter
def list(criteria, assigns, live_resource) do
def list(criteria, fields, assigns, live_resource) do
repo = live_resource.adapter_config(:repo)

list_query(criteria, assigns, live_resource)
list_query(criteria, fields, assigns, live_resource)
|> repo.all()
|> then(fn items -> {:ok, items} end)
end
Expand All @@ -99,10 +99,10 @@ defmodule Backpex.Adapters.Ecto do
Returns the number of items matching the given criteria.
"""
@impl Backpex.Adapter
def count(criteria, assigns, live_resource) do
def count(criteria, fields, assigns, live_resource) do
repo = live_resource.adapter_config(:repo)

list_query(criteria, assigns, live_resource)
list_query(criteria, fields, assigns, live_resource)
|> exclude(:preload)
|> exclude(:select)
|> subquery()
Expand All @@ -115,11 +115,10 @@ defmodule Backpex.Adapters.Ecto do

TODO: Should be private.
"""
def list_query(criteria, assigns, live_resource) do
def list_query(criteria, fields, assigns, live_resource) do
schema = live_resource.adapter_config(:schema)
item_query = live_resource.adapter_config(:item_query)
full_text_search = live_resource.config(:full_text_search)
fields = live_resource.validated_fields()
associations = associations(fields, schema)

schema
Expand Down Expand Up @@ -350,10 +349,9 @@ defmodule Backpex.Adapters.Ecto do

# --- PRIVATE

defp record_query(primary_value, assigns, live_resource) do
defp record_query(primary_value, assigns, fields, live_resource) do
schema = live_resource.adapter_config(:schema)
item_query = live_resource.adapter_config(:item_query)
fields = live_resource.validated_fields()
schema_name = name_by_schema(schema)
primary_key = live_resource.config(:primary_key)
primary_type = schema.__schema__(:type, primary_key)
Expand Down
6 changes: 3 additions & 3 deletions lib/backpex/field.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ defmodule Backpex.Field do
],
only: [
doc: "Define the only views where this field should be visible.",
type: {:list, {:in, [:new, :edit, :show, :index, :resource_action]}}
type: {:list, {:in, [:new, :edit, :show, :index]}}
],
except: [
doc: "Define the views where this field should not be visible.",
type: {:list, {:in, [:new, :edit, :show, :index, :resource_action]}}
type: {:list, {:in, [:new, :edit, :show, :index]}}
],
translate_error: [
doc: """
Expand Down Expand Up @@ -398,7 +398,7 @@ defmodule Backpex.Field do
Handles index editable.
"""
def handle_index_editable(socket, value, change) do
%{assigns: %{item: item, live_resource: live_resource, fields: fields} = assigns} = socket
%{assigns: %{item: item, fields: fields, live_resource: live_resource} = assigns} = socket

if not live_resource.can?(assigns, :edit, item) do
raise Backpex.ForbiddenError
Expand Down
33 changes: 17 additions & 16 deletions lib/backpex/fields/has_many_through.ex
Original file line number Diff line number Diff line change
Expand Up @@ -154,24 +154,18 @@ defmodule Backpex.Fields.HasManyThrough do
<table class="table">
<thead class="bg-base-200/50 text-base-content uppercase">
<tr>
<th
:for={{_name, %{label: label}} <- action_fields(@field_options.child_fields, assigns, :index)}
class="font-medium"
>
<th :for={{_name, %{label: label}} <- action_fields(@field_options.child_fields, :index)} class="font-medium">
{label}
</th>
<th
:for={{_name, %{label: label}} <- action_fields(@field_options.pivot_fields, assigns, :index)}
class="font-medium"
>
<th :for={{_name, %{label: label}} <- action_fields(@field_options.pivot_fields, :index)} class="font-medium">
{label}
</th>
<th></th>
</tr>
</thead>
<tbody class="text-base-content/75">
<tr :for={{listable, index} <- Enum.with_index(@listables)}>
<td :for={{name, field_options} = field <- action_fields(@field_options.child_fields, assigns, :index)}>
<td :for={{name, field_options} = field <- action_fields(@field_options.child_fields, :index)}>
<.live_component
id={"child_table_#{name}_#{index}"}
module={field_options.module}
Expand All @@ -183,7 +177,7 @@ defmodule Backpex.Fields.HasManyThrough do
{assigns}
/>
</td>
<td :for={{name, field_options} = field <- action_fields(@field_options.pivot_fields, assigns, :index)}>
<td :for={{name, field_options} = field <- action_fields(@field_options.pivot_fields, :index)}>
<.live_component
id={"pivot_table_#{name}_#{index}"}
module={field_options.module}
Expand Down Expand Up @@ -252,13 +246,13 @@ defmodule Backpex.Fields.HasManyThrough do
<thead class="bg-base-200/50 text-base-content uppercase">
<tr>
<th
:for={{_name, %{label: label}} <- action_fields(@field_options.child_fields, assigns, :index)}
:for={{_name, %{label: label}} <- action_fields(@field_options.child_fields, :index)}
class="font-medium"
>
{label}
</th>
<th
:for={{_name, %{label: label}} <- action_fields(@field_options.pivot_fields, assigns, :index)}
:for={{_name, %{label: label}} <- action_fields(@field_options.pivot_fields, :index)}
class="font-medium"
>
{label}
Expand All @@ -271,7 +265,7 @@ defmodule Backpex.Fields.HasManyThrough do
:for={{listable, index} <- Enum.with_index(@listables)}
class="border-b-[1px] border-base-content/10 last:border-b-0"
>
<td :for={{name, field_options} <- action_fields(@field_options.child_fields, assigns, :index)}>
<td :for={{name, field_options} <- action_fields(@field_options.child_fields, :index)}>
<.live_component
id={"child_table_#{name}_#{index}"}
module={field_options.module}
Expand All @@ -281,7 +275,7 @@ defmodule Backpex.Fields.HasManyThrough do
{assigns}
/>
</td>
<td :for={{name, field_options} <- action_fields(@field_options.pivot_fields, assigns, :index)}>
<td :for={{name, field_options} <- action_fields(@field_options.pivot_fields, :index)}>
<.live_component
id={"pivot_table_#{name}_#{index}"}
module={field_options.module}
Expand Down Expand Up @@ -414,15 +408,22 @@ defmodule Backpex.Fields.HasManyThrough do
{:noreply, socket}
end

defp action_fields(fields, assigns, action), do: LiveResource.filtered_fields_by_action(fields, assigns, action)
defp action_fields(fields, action) do
# Currently, the fields are only filtered by action, not by the fields `can?` option.
# See https://github.com/naymspace/backpex/pull/1271
LiveResource.fields_by_action(fields, action)
end

defp assign_fallback_child_fields(assigns) do
case Map.has_key?(assigns.field_options, :child_fields) do
true ->
assigns

false ->
fields = assigns.field_options.live_resource.validated_fields()
# It's currently not supported to add a `can?` options to the child fields. Therefore we
# are passing an empty map as the assigns.
# See https://github.com/naymspace/backpex/pull/1271
fields = assigns.field_options.live_resource.fields(:index, %{})
new_field_options = Map.put(assigns.field_options, :child_fields, fields)

assigns
Expand Down
Loading
Loading