Backpex.Field behaviour (Backpex v0.22.0)

Copy Markdown View Source

Behaviour implemented by all fields.

A field defines how a column is rendered on index, show and edit views. In the resource configuration file you can configure a list of fields. You may create your own field by implementing this behaviour. A field has to be a LiveComponent.

Options

These are general field options which can be used on every field. Check the field modules for field-specific options.

  • :module (atom/0) - Required. The field module.

  • :label (String.t/0) - Required. The field label.

  • :readonly - Sets the field to readonly. Also see the readonly guide. The default value is false.

  • :class - Class or classes to be applied when the field is used as a child field in Backpex.Fields.InlineCRUD. The class can be a string or a function that takes the assigns and must return a string.

  • :help_text - A text to be displayed below the input on form views.

  • :default (function of arity 1) - A function to assign default values to fields. Also see the field defaults guide.

  • :render (function of arity 1) - A function to overwrite the template used . It should take assigns and return a HEEX template.

  • :render_form (function of arity 1) - A function to overwrite the template used in forms. It should take assigns and return a HEEX template.

  • :custom_alias (atom/0) - A custom alias for the field.

  • :align - Align the field value on index views.

  • :align_label - Align the label of the field on form views.

  • :searchable (boolean/0) - Define wether this field should be searchable on the index view.

  • :orderable (boolean/0) - Define wether this field should be orderable on the index view.

  • :visible (function of arity 1) - Function to change the visibility of a field for all views except index. Receives the assigns and has to return a boolean.

  • :can? (function of arity 1) - Function to change the visibility of a field for all views. Receives the assigns and has to return a boolean.

  • :panel (atom/0) - Group field into panel. Also see the panels guide.

  • :index_editable - Define wether this field should be editable on the index view. Also see the index edit guide.

  • :index_column_class - Add additional class(es) to the index column. In case of a function it takes the assigns and should return a string.

  • :select (struct of type Ecto.Query.DynamicExpr) - Define a dynamic select query expression for this field.

    Example

    full_name: %{
      module: Backpex.Fields.Text,
      label: "Full Name",
      select: dynamic([user: u], fragment("concat(?, ' ', ?)", u.first_name, u.last_name)),
    }
  • :only - Define the only views where this field should be visible.

  • :except - Define the views where this field should not be visible.

  • :translate_error (function of arity 1) - Function to customize error messages for a field. The function receives the error tuple and must return a tuple with the message and metadata.

Example

def fields do
  [
    rating: %{
      module: Backpex.Fields.Text,
      label: "Rating"
    }
  ]
end

Summary

Callbacks

This function will be called in the FormComponent and may be used to assign uploads.

Determines whether the field is an association or not.

This function is called before the changeset function is called. This allows fields to modify the changeset. The Backpex.Fields.HasMany uses this callback to put the linked associations into the changeset.

Consumes an uploaded entry after the item has been saved.

The field to be displayed on index views. In most cases this is the name / key configured in the corresponding field definition. In fields with associations this value often differs from the name / key. The function will receive the field definition.

Returns assigns for the field on the index view, loaded once for all items of the page.

Returns the change that saves the value of an inline edit on the index view, e.g. %{title: "New title"}.

Returns the files of the item as a list of strings.

Puts the files of an upload field into the params that are passed to the changeset function.

Removes the files the user removed during an edit, after the item has been saved. See list_existing_files/2.

Will be used on edit views to render a form for the value of the provided item. This has to be a heex template.

Used to render form on index to support index editable.

Will be used on index and show views to render a value from the provided item. This has to be a heex template.

The schema to be used in queries. In most cases this is the schema defined in the resource configuration. In fields with associations this is the schema of the corresponding relation. The function will receive the field definition and the schema defined in the resource configuration.

Defines the search condition. Defaults to an ilike condition with text comparison. The function has to return a query wrapped into a Ecto.Query.dynamic/2 which is then passed into a Ecto.Query.where/3.

Functions

Defines Backpex.Field behaviour and provides default implementations.

Assigns the :form and :valid assigns of an index form.

Returns a map of types from a list of fields used for the Ecto changeset.

Defines debounce timeout value.

Returns the default config schema.

Drops readonly field changes from the given change map.

Handles index editable in the field component.

Checks whether index editable is enabled or not.

Defines placeholder value.

Determines whether the field should be rendered as readonly version.

Defines throttle timeout value.

Returns the name of the upload of an upload field, or nil for any other field.

Returns the module that implements the upload callbacks of an upload field.

Callbacks

assign_uploads(field, socket)

@callback assign_uploads(field :: tuple(), socket :: Phoenix.LiveView.Socket.t()) ::
  Phoenix.LiveView.Socket.t()

This function will be called in the FormComponent and may be used to assign uploads.

association?(field)

@callback association?(field :: tuple()) :: boolean()

Determines whether the field is an association or not.

before_changeset(changeset, attrs, metadata, repo, field, assigns)

@callback before_changeset(
  changeset :: Phoenix.LiveView.Socket.t(),
  attrs :: map(),
  metadata :: keyword(),
  repo :: module(),
  field :: tuple(),
  assigns :: map()
) :: Ecto.Changeset.t()

This function is called before the changeset function is called. This allows fields to modify the changeset. The Backpex.Fields.HasMany uses this callback to put the linked associations into the changeset.

consume_upload(field, socket, item, meta, entry)

(optional)
@callback consume_upload(
  field :: tuple(),
  socket :: Phoenix.LiveView.Socket.t(),
  item :: struct(),
  meta :: map(),
  entry :: Phoenix.LiveView.UploadEntry.t()
) :: {:ok, term()} | {:postpone, term()}

Consumes an uploaded entry after the item has been saved.

The form component calls it for every entry as the callback of Phoenix.LiveView.consume_uploaded_entries/3, with the saved item. See list_existing_files/2.

display_field(field)

@callback display_field(field :: tuple()) :: atom()

The field to be displayed on index views. In most cases this is the name / key configured in the corresponding field definition. In fields with associations this value often differs from the name / key. The function will receive the field definition.

index_assigns(field, items, assigns)

(optional)
@callback index_assigns(field :: tuple(), items :: list(), assigns :: map()) :: map()

Returns assigns for the field on the index view, loaded once for all items of the page.

Backpex calls it whenever the items of the index view change (e.g. when loading a page, filtering or on item events) and merges the returned map into the assigns of the field in every row. It receives the field, the items of the page and the assigns of the index view. Use it to load data the field needs, such as the select options of render_index_form/1, with a constant number of queries instead of one query per row and render. Data that differs per row can be returned keyed by the item, e.g. by its primary value.

Also see the index edit guide.

index_editable_change(field, value, assigns)

(optional)
@callback index_editable_change(field :: tuple(), value :: any(), assigns :: map()) ::
  map() | :error

Returns the change that saves the value of an inline edit on the index view, e.g. %{title: "New title"}.

The index view only saves inline edits of fields that implement this callback. It calls it when an index form rendered with Backpex.HTML.Form.index_form/1 changes. It receives the field, the value and the assigns of the index view. Validate or normalize the value here, as the index view saves the returned change with the update changeset of the LiveResource. Return :error to refuse the value, which the index view then marks as invalid.

list_existing_files(field, item)

(optional)
@callback list_existing_files(field :: tuple(), item :: struct() | map()) :: [String.t()]

Returns the files of the item as a list of strings.

Upload fields (see upload_key/1) implement it, together with put_upload_change/7, consume_upload/5 and remove_uploads/4. The form component calls them on the module of the field and passes the field, so a custom upload field can implement them once and work out what it needs from the field, such as the column to read the files from, instead of taking a function per field as an option. Backpex.Fields.Upload implements them by calling the functions given in its options.

put_upload_change(field, socket, params, item, uploaded_entries, removed_entries, action)

(optional)
@callback put_upload_change(
  field :: tuple(),
  socket :: Phoenix.LiveView.Socket.t(),
  params :: map(),
  item :: struct() | map(),
  uploaded_entries :: {list(), list()},
  removed_entries :: [String.t()],
  action :: :validate | :insert
) :: map()

Puts the files of an upload field into the params that are passed to the changeset function.

The form component calls it on every change (action is :validate) and before saving (action is :insert). uploaded_entries is the tuple of completed and in progress entries from Phoenix.LiveView.uploaded_entries/2, removed_entries the files the user removed during an edit. See list_existing_files/2.

remove_uploads(field, socket, item, removed_entries)

(optional)
@callback remove_uploads(
  field :: tuple(),
  socket :: Phoenix.LiveView.Socket.t(),
  item :: struct(),
  removed_entries :: [String.t()]
) :: term()

Removes the files the user removed during an edit, after the item has been saved. See list_existing_files/2.

render_form(assigns)

@callback render_form(assigns :: map()) :: %Phoenix.LiveView.Rendered{
  caller: term(),
  dynamic: term(),
  fingerprint: term(),
  root: term(),
  static: term()
}

Will be used on edit views to render a form for the value of the provided item. This has to be a heex template.

render_index_form(assigns)

(optional)
@callback render_index_form(assigns :: map()) :: %Phoenix.LiveView.Rendered{
  caller: term(),
  dynamic: term(),
  fingerprint: term(),
  root: term(),
  static: term()
}

Used to render form on index to support index editable.

render_value(assigns)

@callback render_value(assigns :: map()) :: %Phoenix.LiveView.Rendered{
  caller: term(),
  dynamic: term(),
  fingerprint: term(),
  root: term(),
  static: term()
}

Will be used on index and show views to render a value from the provided item. This has to be a heex template.

schema(field, schema)

@callback schema(field :: tuple(), schema :: atom()) :: atom()

The schema to be used in queries. In most cases this is the schema defined in the resource configuration. In fields with associations this is the schema of the corresponding relation. The function will receive the field definition and the schema defined in the resource configuration.

search_condition(schema_name, field_name, search_string)

@callback search_condition(
  schema_name :: binary(),
  field_name :: binary(),
  search_string :: binary()
) ::
  Ecto.Query.dynamic_expr()

Defines the search condition. Defaults to an ilike condition with text comparison. The function has to return a query wrapped into a Ecto.Query.dynamic/2 which is then passed into a Ecto.Query.where/3.

Example

Imagine the underlying database type of the field is an integer. Before text comparison in an ilike condition you have to cast the integer to text.

The function could return the following query to make the field searchable.

dynamic(
  [{^schema_name, schema_name}],
  ilike(fragment("CAST(? AS TEXT)", schema_name |> field(^field_name)), ^search_string)
)

Functions

__using__(opts)

(macro)

Defines Backpex.Field behaviour and provides default implementations.

align_label(field_options, assigns, default \\ :center)

Gets alignment option for label.

assign_index_form(assigns)

Assigns the :form and :valid assigns of an index form.

The form holds the value of the item, or the value of an inline edit the index view could not save, which is then also marked as invalid. Call it in render_index_form/1 before rendering Backpex.HTML.Form.index_form/1.

changeset_types(fields)

Returns a map of types from a list of fields used for the Ecto changeset.

debounce(arg1, assigns)

Defines debounce timeout value.

default_config_schema()

Returns the default config schema.

drop_readonly_changes(change, fields, assigns)

Drops readonly field changes from the given change map.

Takes a map of string-keyed form params, a keyword list of field definitions, and assigns. Returns the change map with readonly field keys removed.

handle_index_editable(socket, value, change)

Handles index editable in the field component.

Index forms rendered with Backpex.HTML.Form.index_form/1 of fields that implement index_editable_change/3 are saved by the index view instead, with all of its assigns. This function saves the change with the assigns of the field component. With a list of :context_assigns, these only include the listed assigns, see the :context_assigns option of Backpex.LiveResource.

The change is not saved if the field is not index editable or readonly, if the user may not edit the item, or while a resource action is open, as the adapter would use the changeset of the resource action. The value is then kept in the form and marked as invalid.

help_text(arg1, assigns)

index_editable_enabled?(field_options, assigns, default \\ false)

Checks whether index editable is enabled or not.

placeholder(arg1, assigns)

Defines placeholder value.

readonly?(arg1, assigns)

Determines whether the field should be rendered as readonly version.

throttle(arg1, assigns)

Defines throttle timeout value.

translate_error_fun(arg1, assigns)

upload_key(arg1)

@spec upload_key(tuple()) :: atom() | nil

Returns the name of the upload of an upload field, or nil for any other field.

An upload field is a field with an :upload_key option, which names its upload, or a field whose module implements the upload callbacks, such as consume_upload/5. The upload of such a field is named after the field when the option isn't given.

Examples

iex> Backpex.Field.upload_key({:avatar, %{module: Backpex.Fields.Upload}})
:avatar
iex> Backpex.Field.upload_key({:avatar, %{module: Backpex.Fields.Upload, upload_key: :picture}})
:picture
iex> Backpex.Field.upload_key({:avatar, %{module: Backpex.Fields.Text, upload_key: :picture}})
:picture
iex> Backpex.Field.upload_key({:title, %{module: Backpex.Fields.Text}})
nil

upload_module(field)

@spec upload_module(tuple()) :: module()

Returns the module that implements the upload callbacks of an upload field.

This is the module of the field when it implements consume_upload/5 and the other upload callbacks, otherwise Backpex.Fields.Upload, which calls the :list_existing_files, :put_upload_change, :consume_upload and :remove_uploads functions of the field options. So a custom field that only defines an :upload_key and these options, as before the callbacks existed, keeps working.

Examples

iex> Backpex.Field.upload_module({:avatar, %{module: Backpex.Fields.Upload}})
Backpex.Fields.Upload
iex> Backpex.Field.upload_module({:avatar, %{module: Backpex.Fields.Text}})
Backpex.Fields.Upload