diff --git a/demo/lib/demo_web/live/post_live.ex b/demo/lib/demo_web/live/post_live.ex index ebf78aa06..40be4dd20 100644 --- a/demo/lib/demo_web/live/post_live.ex +++ b/demo/lib/demo_web/live/post_live.ex @@ -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: %{ @@ -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, diff --git a/demo/lib/demo_web/live/product_live.ex b/demo/lib/demo_web/live/product_live.ex index a3350d7e2..1465bceb3 100644 --- a/demo/lib/demo_web/live/product_live.ex +++ b/demo/lib/demo_web/live/product_live.ex @@ -51,7 +51,7 @@ defmodule DemoWeb.ProductLive do assigns -> ~H"
{Backpex.HTML.pretty_value(@value)}
" end, - except: [:index, :resource_action] + except: [:index] }, name: %{ module: Backpex.Fields.Text, diff --git a/demo/lib/demo_web/live/short_link_live.ex b/demo/lib/demo_web/live/short_link_live.ex index 14e9e3da5..c774a21d0 100644 --- a/demo/lib/demo_web/live/short_link_live.ex +++ b/demo/lib/demo_web/live/short_link_live.ex @@ -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" diff --git a/guides/fields/visibility.md b/guides/fields/visibility.md index a870da1f3..8ee90bb11 100644 --- a/guides/fields/visibility.md +++ b/guides/fields/visibility.md @@ -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 diff --git a/guides/filter/how-to-add-a-filter.md b/guides/filter/how-to-add-a-filter.md index eb3d1a79b..a03d2f5c0 100644 --- a/guides/filter/how-to-add-a-filter.md +++ b/guides/filter/how-to-add-a-filter.md @@ -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, diff --git a/guides/live_resource/navigation.md b/guides/live_resource/navigation.md index 59de432f1..75ad800aa 100644 --- a/guides/live_resource/navigation.md +++ b/guides/live_resource/navigation.md @@ -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`. \ No newline at end of file +For all other live actions, the form_action will be `nil`. diff --git a/guides/upgrading/v0.15.md b/guides/upgrading/v0.15.md index 94ef7ad4e..8a6557d65 100644 --- a/guides/upgrading/v0.15.md +++ b/guides/upgrading/v0.15.md @@ -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. diff --git a/guides/upgrading/v0.7.md b/guides/upgrading/v0.7.md index dc7df0cb6..65bbba358 100644 --- a/guides/upgrading/v0.7.md +++ b/guides/upgrading/v0.7.md @@ -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. @@ -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). \ No newline at end of file +Read more about the new `item` parameter in [the item action guide](/guides/actions/item-actions.md#implementing-an-item-action). diff --git a/lib/backpex/adapter.ex b/lib/backpex/adapter.ex index 274783dca..cad675185 100644 --- a/lib/backpex/adapter.ex +++ b/lib/backpex/adapter.ex @@ -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 diff --git a/lib/backpex/adapters/ash.ex b/lib/backpex/adapters/ash.ex index ab7d259d1..8e501e4e1 100644 --- a/lib/backpex/adapters/ash.ex +++ b/lib/backpex/adapters/ash.ex @@ -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) @@ -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 @@ -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 diff --git a/lib/backpex/adapters/ecto.ex b/lib/backpex/adapters/ecto.ex index 3c4b1b982..b76425dc9 100644 --- a/lib/backpex/adapters/ecto.ex +++ b/lib/backpex/adapters/ecto.ex @@ -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 @@ -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 @@ -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() @@ -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 @@ -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) diff --git a/lib/backpex/field.ex b/lib/backpex/field.ex index 552183acd..89bb3a0fb 100644 --- a/lib/backpex/field.ex +++ b/lib/backpex/field.ex @@ -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: """ @@ -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 diff --git a/lib/backpex/fields/has_many_through.ex b/lib/backpex/fields/has_many_through.ex index da63984d5..04f1ebb87 100644 --- a/lib/backpex/fields/has_many_through.ex +++ b/lib/backpex/fields/has_many_through.ex @@ -154,16 +154,10 @@ defmodule Backpex.Fields.HasManyThrough do| + | {label} | -+ | {label} | @@ -171,7 +165,7 @@ defmodule Backpex.Fields.HasManyThrough do | |||||
|---|---|---|---|---|---|---|---|---|---|
| + | <.live_component id={"child_table_#{name}_#{index}"} module={field_options.module} @@ -183,7 +177,7 @@ defmodule Backpex.Fields.HasManyThrough do {assigns} /> | -+ |
<.live_component
id={"pivot_table_#{name}_#{index}"}
module={field_options.module}
@@ -252,13 +246,13 @@ defmodule Backpex.Fields.HasManyThrough do
|
{label}
|
{label}
@@ -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"
>
- |
+ |
<.live_component
id={"child_table_#{name}_#{index}"}
module={field_options.module}
@@ -281,7 +275,7 @@ defmodule Backpex.Fields.HasManyThrough do
{assigns}
/>
|
-
+ |
<.live_component
id={"pivot_table_#{name}_#{index}"}
module={field_options.module}
@@ -414,7 +408,11 @@ 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
@@ -422,7 +420,10 @@ defmodule Backpex.Fields.HasManyThrough do
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
diff --git a/lib/backpex/filters/select.ex b/lib/backpex/filters/select.ex
index 051b780c6..b0174a919 100644
--- a/lib/backpex/filters/select.ex
+++ b/lib/backpex/filters/select.ex
@@ -1,6 +1,6 @@
defmodule Backpex.Filters.Select do
@moduledoc """
- Tbe select filter renders a select box for the implemented `options/0` and `prompt/0` callbacks. The `prompt/0` callback defines the key for the `nil` value added as first option.
+ The select filter renders a select box for the implemented `options/1` and `prompt/0` callbacks. The `prompt/0` callback defines the key for the `nil` value added as first option.
See the following example for an implementation of an event status filter.
@@ -14,7 +14,7 @@ defmodule Backpex.Filters.Select do
def prompt, do: "Select an option..."
@impl Backpex.Filters.Select
- def options, do: [
+ def options(_assigns), do: [
{"Open", :open},
{"Close", :close},
]
diff --git a/lib/backpex/html/resource.ex b/lib/backpex/html/resource.ex
index dcd4500f6..7ba505b53 100644
--- a/lib/backpex/html/resource.ex
+++ b/lib/backpex/html/resource.ex
@@ -102,7 +102,7 @@ defmodule Backpex.HTML.Resource do
attr :fields, :list, required: true, doc: "list of all fields provided by the resource configuration"
def resource_field(assigns) do
- %{name: name, item: item, fields: fields, live_resource: live_resource} = assigns
+ %{name: name, item: item, live_resource: live_resource, fields: fields} = assigns
{_name, field_options} = field = Enum.find(fields, fn {field_name, _field_options} -> field_name == name end)
diff --git a/lib/backpex/html/resource/resource_index.html.heex b/lib/backpex/html/resource/resource_index.html.heex
index 922639564..0e3fde187 100644
--- a/lib/backpex/html/resource/resource_index.html.heex
+++ b/lib/backpex/html/resource/resource_index.html.heex
@@ -12,7 +12,7 @@
id={:modal}
live_resource={@live_resource}
action_type={:resource}
- {Map.drop(assigns, [:socket, :flash])}
+ {Map.drop(assigns, [:socket, :flash, :fields])}
/>
diff --git a/lib/backpex/live_components/form_component.ex b/lib/backpex/live_components/form_component.ex
index 3fe455173..bdf7cacf0 100644
--- a/lib/backpex/live_components/form_component.ex
+++ b/lib/backpex/live_components/form_component.ex
@@ -21,11 +21,26 @@ defmodule Backpex.FormComponent do
|> ok()
end
+ # item action
defp update_assigns(%{assigns: %{action_type: :item}} = socket) do
+ %{action_to_confirm: action_to_confirm} = socket.assigns
+
socket
- |> assign_fields()
+ |> assign_new(:fields, fn -> action_to_confirm.module.fields() end)
+ |> assign(:save_label, action_to_confirm.module.confirm_label(socket.assigns))
end
+ # resource action
+ defp update_assigns(%{assigns: %{action_type: :resource}} = socket) do
+ %{resource_action: resource_action} = socket.assigns
+
+ socket
+ |> assign_new(:fields, fn -> resource_action.module.fields() end)
+ |> assign(:save_label, ResourceAction.name(resource_action, :label))
+ |> maybe_assign_uploads()
+ end
+
+ # default form
defp update_assigns(%{assigns: assigns} = socket) do
socket
|> apply_action(assigns.live_action)
@@ -41,26 +56,12 @@ defmodule Backpex.FormComponent do
assign_new(socket, :removed_uploads, fn -> Keyword.new() end)
end
- defp assign_fields(%{assigns: %{action_to_confirm: action_to_confirm}} = socket) do
- socket
- |> assign_new(:fields, fn -> action_to_confirm.module.fields() end)
- |> assign(:save_label, action_to_confirm.module.confirm_label(socket.assigns))
- end
-
defp apply_action(socket, action) when action in [:edit, :new] do
socket
|> assign(:save_label, Backpex.__("Save", socket.assigns.live_resource))
|> maybe_assign_continue_label()
end
- defp apply_action(socket, :resource_action) do
- %{assigns: %{resource_action: resource_action}} = socket
-
- socket
- |> assign(:save_label, ResourceAction.name(resource_action, :label))
- |> assign(:fields, resource_action.module.fields())
- end
-
defp maybe_assign_continue_label(socket) do
case socket.assigns.live_resource.config(:save_and_continue_button?) do
true -> assign(socket, :continue_label, Backpex.__("Save & Continue editing", socket.assigns.live_resource))
@@ -77,7 +78,6 @@ defmodule Backpex.FormComponent do
def handle_event("validate", %{"change" => change, "_target" => target}, %{assigns: %{action_type: :item}} = socket) do
%{assigns: %{item: item, fields: fields} = assigns} = socket
-
changeset_function = &assigns.action_to_confirm.module.changeset/3
target = Enum.at(target, 1)
@@ -105,11 +105,7 @@ defmodule Backpex.FormComponent do
end
def handle_event("validate", %{"change" => change, "_target" => target}, socket) do
- %{
- live_resource: live_resource,
- item: item,
- fields: fields
- } = socket.assigns
+ %{live_resource: live_resource, fields: fields, item: item} = socket.assigns
target = Enum.at(target, 1)
assocs = Map.get(socket.assigns, :assocs, [])
@@ -149,7 +145,7 @@ defmodule Backpex.FormComponent do
upload_key = String.to_existing_atom(upload_key)
field =
- socket.assigns.fields()
+ socket.assigns.fields
|> Enum.find(fn {_name, field_options} ->
Map.has_key?(field_options, :upload_key) and Map.get(field_options, :upload_key) == upload_key
end)
@@ -207,7 +203,7 @@ defmodule Backpex.FormComponent do
defp handle_save(socket, key, params, save_type \\ "save")
defp handle_save(socket, :new, params, save_type) do
- %{assigns: %{live_resource: live_resource, item: item, live_action: live_action} = assigns} = socket
+ %{assigns: %{live_resource: live_resource, fields: fields, item: item, live_action: live_action} = assigns} = socket
opts = [
assocs: Map.get(assigns, :assocs, []),
@@ -219,7 +215,7 @@ defmodule Backpex.FormComponent do
end
]
- case Resource.insert(item, params, socket.assigns, live_resource, opts) do
+ case Resource.insert(item, params, fields, socket.assigns, live_resource, opts) do
{:ok, item} ->
return_to = return_to_path(save_type, live_resource, socket, socket.assigns, live_action, item)
@@ -252,8 +248,8 @@ defmodule Backpex.FormComponent do
%{
live_resource: live_resource,
item: item,
- fields: fields,
- live_action: live_action
+ live_action: live_action,
+ fields: fields
} = socket.assigns
opts = [
@@ -300,10 +296,10 @@ defmodule Backpex.FormComponent do
assigns:
%{
live_resource: live_resource,
+ fields: fields,
resource_action: resource_action,
item: item,
- return_to: return_to,
- fields: fields
+ return_to: return_to
} = assigns
} = socket
@@ -351,9 +347,9 @@ defmodule Backpex.FormComponent do
assigns:
%{
live_resource: live_resource,
+ fields: fields,
selected_items: selected_items,
action_to_confirm: action_to_confirm,
- fields: fields,
return_to: return_to
} = assigns
} = socket
diff --git a/lib/backpex/live_resource.ex b/lib/backpex/live_resource.ex
index 3ef39410d..8d5296f38 100644
--- a/lib/backpex/live_resource.ex
+++ b/lib/backpex/live_resource.ex
@@ -274,7 +274,7 @@ defmodule Backpex.LiveResource do
def pubsub, do: LiveResource.pubsub(__MODULE__)
- def validated_fields, do: LiveResource.validated_fields(__MODULE__)
+ def fields(live_action, assigns), do: LiveResource.fields(__MODULE__, live_action, assigns)
@impl Backpex.LiveResource
def can?(_assigns, _action, _item), do: true
@@ -474,9 +474,19 @@ defmodule Backpex.LiveResource do
end
@doc """
- Returns the fields of the given `Backpex.LiveResource` validated against each fields config schema.
+ Returns the fields of the given `Backpex.LiveResource`.
+
+ Each field is validated against each fields config schema and filtered by the `live_action` and
+ the fields `can?` options.
"""
- def validated_fields(live_resource) do
+ def fields(live_resource, live_action, assigns) do
+ live_resource
+ |> validated_fields()
+ |> fields_by_action(live_action)
+ |> fields_by_can(assigns)
+ end
+
+ defp validated_fields(live_resource) do
live_resource.fields()
|> Enum.map(fn {name, options} = field ->
options.module.validate_config!(field, live_resource)
@@ -612,17 +622,40 @@ defmodule Backpex.LiveResource do
Returns filtered fields by a certain action.
## Example
- iex> Backpex.LiveResource.filtered_fields_by_action([field1: %{label: "Field1"}, field2: %{label: "Field2"}], %{}, :index)
+ iex> Backpex.LiveResource.fields_by_action([field1: %{label: "Field1"}, field2: %{label: "Field2"}], :index)
[field1: %{label: "Field1"}, field2: %{label: "Field2"}]
- iex> Backpex.LiveResource.filtered_fields_by_action([field1: %{label: "Field1", except: [:show]}, field2: %{label: "Field2"}], %{}, :show)
+ iex> Backpex.LiveResource.fields_by_action([field1: %{label: "Field1", except: [:show]}, field2: %{label: "Field2"}], :show)
[field2: %{label: "Field2"}]
- iex> Backpex.LiveResource.filtered_fields_by_action([field1: %{label: "Field1", only: [:index]}, field2: %{label: "Field2"}], %{}, :show)
+ iex> Backpex.LiveResource.fields_by_action([field1: %{label: "Field1", only: [:index]}, field2: %{label: "Field2"}], :show)
+ [field2: %{label: "Field2"}]
+ """
+ def fields_by_action(fields, :resource_action), do: fields_by_action(fields, :index)
+
+ def fields_by_action(fields, action) do
+ fields
+ |> Keyword.filter(fn {_name, field_options} ->
+ filter_field_by_action(field_options, action)
+ end)
+ end
+
+ @doc """
+ Returns filtered fields by the result of the implemented `can?` function.
+
+ ## Example
+ > Backpex.LiveResource.fields_by_can([field1: %{label: "Field1"}], %{})
+ [field1: %{label: "Field1"}]
+ > Backpex.LiveResource.fields_by_can([field1: %{label: "Field1", can?: fn _assigns -> true end}, field2: %{label: "Field2", can?: fn _assigns -> true end}], %{})
+ [field1: %{label: "Field1"}, field2: %{label: "Field2"}]
+ > Backpex.LiveResource.fields_by_can([field1: %{label: "Field1", can?: fn _assigns -> false end}, field2: %{label: "Field2", can?: fn _assigns -> true end}], %{})
[field2: %{label: "Field2"}]
+ > Backpex.LiveResource.fields_by_can([field1: %{label: "Field1", can?: fn _assigns -> false end}], %{})
+ []
+
"""
- def filtered_fields_by_action(fields, assigns, action) do
+ def fields_by_can(fields, assigns) do
fields
|> Keyword.filter(fn {_name, field_options} ->
- can_view_field?(field_options, assigns) and filter_field_by_action(field_options, action)
+ can_view_field?(field_options, assigns)
end)
end
@@ -692,13 +725,14 @@ defmodule Backpex.LiveResource do
def build_criteria(assigns) do
%{
live_resource: live_resource,
- fields: fields,
filters: filters,
query_options: query_options,
- init_order: init_order
+ init_order: init_order,
+ fields: fields
} = assigns
schema = live_resource.adapter_config(:schema)
+
field = Enum.find(fields, fn {name, _field_options} -> name == query_options.order_by end)
order =
diff --git a/lib/backpex/live_resource/form.ex b/lib/backpex/live_resource/form.ex
index 8bf382e90..d133a421b 100644
--- a/lib/backpex/live_resource/form.ex
+++ b/lib/backpex/live_resource/form.ex
@@ -16,9 +16,9 @@ defmodule Backpex.LiveResource.Form do
|> assign(:live_resource, live_resource)
|> assign(:panels, live_resource.panels())
|> assign(:fluid?, live_resource.config(:fluid?))
+ |> assign(:fields, live_resource.fields(live_action, socket.assigns))
|> assign(:params, params)
|> assign(:page_title, page_title(live_resource, live_action))
- |> assign_fields(live_action)
|> assign_item(live_action)
|> can?(live_resource, live_action)
|> assign_changeset(live_action)
@@ -75,12 +75,12 @@ defmodule Backpex.LiveResource.Form do
end
defp assign_item(socket, :edit = _live_action) do
- %{live_resource: live_resource, params: params} = socket.assigns
+ %{live_resource: live_resource, fields: fields, params: params} = socket.assigns
backpex_id = Map.fetch!(params, "backpex_id")
primary_value = URI.decode(backpex_id)
- item = Resource.get!(primary_value, socket.assigns, live_resource)
+ item = Resource.get!(primary_value, fields, socket.assigns, live_resource)
assign(socket, :item, item)
end
@@ -97,18 +97,10 @@ defmodule Backpex.LiveResource.Form do
socket
end
- defp assign_fields(socket, live_action) do
- fields =
- socket.assigns.live_resource.validated_fields()
- |> LiveResource.filtered_fields_by_action(socket.assigns, live_action)
-
- assign(socket, :fields, fields)
- end
-
defp assign_changeset(socket, live_action) do
%{live_resource: live_resource, item: item, fields: fields} = socket.assigns
- changeset_fun = changeset_fun(live_resource, live_action)
+ changeset_fun = changeset_fun(live_resource, live_action)
LiveResource.assign_changeset(socket, changeset_fun, item, fields, live_action)
end
diff --git a/lib/backpex/live_resource/index.ex b/lib/backpex/live_resource/index.ex
index e37b7f535..95cd70055 100644
--- a/lib/backpex/live_resource/index.ex
+++ b/lib/backpex/live_resource/index.ex
@@ -24,6 +24,7 @@ defmodule Backpex.LiveResource.Index do
|> assign(:live_resource, live_resource)
|> assign(:panels, live_resource.panels())
|> assign(:fluid?, live_resource.config(:fluid?))
+ |> assign(:fields, live_resource.fields(socket.assigns.live_action, socket.assigns))
|> assign(
:create_button_label,
Backpex.__({"New %{resource}", %{resource: live_resource.singular_name()}}, live_resource)
@@ -334,11 +335,9 @@ defmodule Backpex.LiveResource.Index do
end
defp assign_active_fields(socket, session) do
- fields =
- socket.assigns.live_resource.validated_fields()
- |> LiveResource.filtered_fields_by_action(socket.assigns, :index)
+ %{fields: fields, live_resource: live_resource} = socket.assigns
- saved_fields = get_in(session, ["backpex", "column_toggle", "#{socket.assigns.live_resource}"]) || %{}
+ saved_fields = get_in(session, ["backpex", "column_toggle", "#{live_resource}"]) || %{}
active_fields =
Enum.map(fields, fn {name, %{label: label}} ->
@@ -349,8 +348,7 @@ defmodule Backpex.LiveResource.Index do
}}
end)
- socket
- |> assign(:active_fields, active_fields)
+ assign(socket, :active_fields, active_fields)
end
defp field_active?(name, saved_fields) do
@@ -362,12 +360,11 @@ defmodule Backpex.LiveResource.Index do
end
defp update_item(socket, item) do
- %{live_resource: live_resource, items: items} = socket.assigns
+ %{live_resource: live_resource, fields: fields, items: items} = socket.assigns
primary_value = LiveResource.primary_value(item, live_resource)
primary_value_str = to_string(primary_value)
-
- {:ok, updated_item} = Resource.get(primary_value, socket.assigns, live_resource)
+ {:ok, updated_item} = Resource.get(primary_value, fields, socket.assigns, live_resource)
updated_items =
Enum.map(items, fn current_item ->
@@ -431,12 +428,10 @@ defmodule Backpex.LiveResource.Index do
end
defp apply_index(socket) do
- %{live_resource: live_resource, params: params} = socket.assigns
+ %{live_resource: live_resource, params: params, fields: fields} = socket.assigns
if not live_resource.can?(socket.assigns, :index, nil), do: raise(Backpex.ForbiddenError)
- fields = live_resource.validated_fields() |> LiveResource.filtered_fields_by_action(socket.assigns, :index)
-
per_page_options = live_resource.config(:per_page_options)
per_page_default = live_resource.config(:per_page_default)
init_order = live_resource.config(:init_order)
@@ -451,7 +446,7 @@ defmodule Backpex.LiveResource.Index do
filters: LiveResource.filter_options(valid_filter_params, filters)
]
- {:ok, item_count} = Resource.count(count_criteria, socket.assigns, live_resource)
+ {:ok, item_count} = Resource.count(count_criteria, fields, socket.assigns, live_resource)
per_page =
params
@@ -459,8 +454,8 @@ defmodule Backpex.LiveResource.Index do
|> LiveResource.value_in_permitted_or_default(per_page_options, per_page_default)
total_pages = LiveResource.calculate_total_pages(item_count, per_page)
- page = params |> LiveResource.parse_integer("page", 1) |> LiveResource.validate_page(total_pages)
+ page = params |> LiveResource.parse_integer("page", 1) |> LiveResource.validate_page(total_pages)
page_options = %{page: page, per_page: per_page}
order_options = LiveResource.order_options_by_params(params, fields, init_order, socket.assigns)
@@ -485,7 +480,6 @@ defmodule Backpex.LiveResource.Index do
|> assign(:action_to_confirm, nil)
|> assign(:selected_items, [])
|> assign(:select_all, false)
- |> assign(:fields, fields)
|> maybe_redirect_to_default_filters()
|> assign_items()
|> maybe_assign_metrics()
@@ -550,12 +544,7 @@ defmodule Backpex.LiveResource.Index do
end
defp refresh_items(socket) do
- %{
- live_resource: live_resource,
- params: params,
- fields: fields,
- query_options: query_options
- } = socket.assigns
+ %{live_resource: live_resource, params: params, query_options: query_options, fields: fields} = socket.assigns
schema = live_resource.adapter_config(:schema)
filters = LiveResource.active_filters(socket.assigns)
@@ -566,7 +555,7 @@ defmodule Backpex.LiveResource.Index do
filters: LiveResource.filter_options(valid_filter_params, filters)
]
- {:ok, item_count} = Resource.count(count_criteria, socket.assigns, live_resource)
+ {:ok, item_count} = Resource.count(count_criteria, fields, socket.assigns, live_resource)
%{page: page, per_page: per_page} = query_options
total_pages = LiveResource.calculate_total_pages(item_count, per_page)
new_query_options = Map.put(query_options, :page, LiveResource.validate_page(page, total_pages))
@@ -582,9 +571,9 @@ defmodule Backpex.LiveResource.Index do
defp maybe_assign_metrics(socket) do
%{
live_resource: live_resource,
- fields: fields,
query_options: query_options,
- metric_visibility: metric_visibility
+ metric_visibility: metric_visibility,
+ fields: fields
} = socket.assigns
repo = live_resource.adapter_config(:repo)
@@ -599,7 +588,7 @@ defmodule Backpex.LiveResource.Index do
filters: LiveResource.filter_options(query_options, filters)
]
- query = EctoAdapter.list_query(criteria, socket.assigns, live_resource)
+ query = EctoAdapter.list_query(criteria, fields, socket.assigns, live_resource)
case Backpex.Metric.metrics_visible?(metric_visibility, live_resource) do
true ->
@@ -622,8 +611,12 @@ defmodule Backpex.LiveResource.Index do
end
defp assign_items(socket) do
- criteria = LiveResource.build_criteria(socket.assigns)
- {:ok, items} = Resource.list(criteria, socket.assigns, socket.assigns.live_resource)
+ %{assigns: %{live_resource: live_resource, fields: fields} = assigns} = socket
+
+ {:ok, items} =
+ assigns
+ |> LiveResource.build_criteria()
+ |> Resource.list(fields, assigns, live_resource)
assign(socket, :items, items)
end
diff --git a/lib/backpex/live_resource/show.ex b/lib/backpex/live_resource/show.ex
index 3a6515ae5..ba3b303c8 100644
--- a/lib/backpex/live_resource/show.ex
+++ b/lib/backpex/live_resource/show.ex
@@ -4,7 +4,6 @@ defmodule Backpex.LiveResource.Show do
import Phoenix.Component
- alias Backpex.LiveResource
alias Backpex.Resource
alias Backpex.Router
alias Phoenix.LiveView
@@ -22,9 +21,9 @@ defmodule Backpex.LiveResource.Show do
|> assign(:live_resource, live_resource)
|> assign(:panels, live_resource.panels())
|> assign(:fluid?, live_resource.config(:fluid?))
+ |> assign(:fields, live_resource.fields(:show, socket.assigns))
|> assign(:page_title, live_resource.singular_name())
|> assign(:params, params)
- |> assign_fields()
|> assign_item()
|> ok()
end
@@ -52,12 +51,10 @@ defmodule Backpex.LiveResource.Show do
end
defp assign_item(socket) do
- %{live_resource: live_resource, params: params} = socket.assigns
-
+ %{live_resource: live_resource, fields: fields, params: params} = socket.assigns
backpex_id = Map.fetch!(params, "backpex_id")
primary_value = URI.decode(backpex_id)
-
- item = Resource.get!(primary_value, socket.assigns, live_resource)
+ item = Resource.get!(primary_value, fields, socket.assigns, live_resource)
if not live_resource.can?(socket.assigns, :show, item), do: raise(Backpex.ForbiddenError)
@@ -65,12 +62,4 @@ defmodule Backpex.LiveResource.Show do
|> assign(:item, item)
|> assign(:return_to, Router.get_path(socket, live_resource, params, :show, item))
end
-
- defp assign_fields(socket) do
- fields =
- socket.assigns.live_resource.validated_fields()
- |> LiveResource.filtered_fields_by_action(socket.assigns, :show)
-
- assign(socket, :fields, fields)
- end
end
diff --git a/lib/backpex/resource.ex b/lib/backpex/resource.ex
index 9bc355560..b62b767a4 100644
--- a/lib/backpex/resource.ex
+++ b/lib/backpex/resource.ex
@@ -19,20 +19,20 @@ defmodule Backpex.Resource do
search: {"hello", [:title, :description]}
]
"""
- def list(criteria, assigns, live_resource) do
+ def list(criteria, fields, assigns, live_resource) do
adapter = live_resource.config(:adapter)
- adapter.list(criteria, assigns, live_resource)
+ adapter.list(criteria, fields, assigns, live_resource)
end
@doc """
Gets the total count of the current live_resource.
Possibly being constrained the item query and the search- and filter options.
"""
- def count(criteria, assigns, live_resource) do
+ def count(criteria, fields, assigns, live_resource) do
adapter = live_resource.config(:adapter)
- adapter.count(criteria, assigns, live_resource)
+ adapter.count(criteria, fields, assigns, live_resource)
end
@doc """
@@ -46,17 +46,17 @@ defmodule Backpex.Resource do
* `assigns` (map): The current assigns of the socket.
* `live_resource` (module): The `Backpex.LiveResource` module.
"""
- def get(primary_value, assigns, live_resource) do
+ def get(primary_value, fields, assigns, live_resource) do
adapter = live_resource.config(:adapter)
- adapter.get(primary_value, assigns, live_resource)
+ adapter.get(primary_value, fields, assigns, live_resource)
end
@doc """
- Same as `get/3` but returns the result or raises an error.
+ Same as `get/4` but returns the result or raises an error.
"""
- def get!(primary_value, assigns, live_resource) do
- case get(primary_value, assigns, live_resource) do
+ def get!(primary_value, fields, assigns, live_resource) do
+ case get(primary_value, fields, assigns, live_resource) do
{:ok, nil} -> raise Backpex.NoResultsError
{:ok, result} -> result
{:error, _error} -> raise Backpex.NoResultsError
@@ -92,11 +92,10 @@ defmodule Backpex.Resource do
* `attrs` (map): A map of parameters that will be passed to the `changeset_function`.
* TODO: docs
"""
- def insert(item, attrs, assigns, live_resource, opts) do
+ def insert(item, attrs, fields, assigns, live_resource, opts) do
{after_save_fun, opts} = Keyword.pop(opts, :after_save_fun, &{:ok, &1})
adapter = live_resource.config(:adapter)
- fields = live_resource.validated_fields()
item
|> change(attrs, fields, assigns, live_resource, Keyword.put(opts, :action, :insert))
| |