MishkaGervaz.Table.Web.DataLoader.RelationLoader behaviour (MishkaGervaz v0.0.2)

Copy Markdown View Source

Handles paginated loading of relation filter options.

Provides support for different loading modes:

  • :static - Load all options at once
  • :load_more - Initial page with load more capability
  • :search - Search with pagination (single select)
  • :search_multi - Search with pagination (multi select)

A fixed list instead of a resource

A :relation filter with no relationship or resource behind it that declares options — a list, or a zero-arity function returning one — is served from that list: loaded whole, searched by label or value in memory, and its selection read back from it. It is how a list the database does not hold as rows — the languages a site publishes — gets the searchable multi-select:

filter :language, :relation do
  mode :search_multi
  options fn -> [{"EN", "en"}, {"FA", "fa"}] end
end

Usage

# Load initial options
{:ok, result} = RelationLoader.load_options(filter, state, page: 1)

# Search options
{:ok, result} = RelationLoader.search_options(filter, state, "search term", page: 1)

Override

defmodule MyApp.Table.RelationLoader do
  use MishkaGervaz.Table.Web.DataLoader.RelationLoader

  def load_options(filter, state, opts) do
    # Custom loading logic
    super(filter, state, opts)
  end
end

See MishkaGervaz.Table.Web.DataLoader, MishkaGervaz.Table.Web.DataLoader.Helpers, MishkaGervaz.Table.Types.Filter.Relation, and the sibling sub-builders QueryBuilder, FilterParser, PaginationHandler, TenantResolver, HookRunner.

Summary

Functions

The filter's own option list, normalized to {label, value} — or nil when a relationship or a resource supplies its options. A relation filter's options alone says nothing: the table fills it with the options it loaded from the relationship.

A page of a static option list: all of it, or the options whose label or value contains term, ignoring case.

Types

filter()

@type filter() :: map()

load_result()

@type load_result() :: %{
  options: [{String.t(), any()}],
  page: pos_integer(),
  has_more?: boolean(),
  total_count: pos_integer() | nil
}

state()

@type state() :: map()

Callbacks

load_more_options(filter, state, keyword)

@callback load_more_options(filter(), state(), keyword()) ::
  {:ok, load_result()} | {:error, term()}

load_options(filter, state, keyword)

@callback load_options(filter(), state(), keyword()) ::
  {:ok, load_result()} | {:error, term()}

load_with_selected(filter, state, list, keyword)

@callback load_with_selected(filter(), state(), list(), keyword()) ::
  {:ok, load_result()} | {:error, term()}

resolve_selected(filter, state, list)

@callback resolve_selected(filter(), state(), list()) ::
  {:ok, [{String.t(), any()}]} | {:error, term()}

search_options(filter, state, t, keyword)

@callback search_options(filter(), state(), String.t(), keyword()) ::
  {:ok, load_result()} | {:error, term()}

Functions

static_options(filter, state)

@spec static_options(map(), map()) :: [{String.t(), any()}] | nil

The filter's own option list, normalized to {label, value} — or nil when a relationship or a resource supplies its options. A relation filter's options alone says nothing: the table fills it with the options it loaded from the relationship.

static_page(filter, options, term)

@spec static_page(map(), [{String.t(), any()}], String.t() | nil) :: map()

A page of a static option list: all of it, or the options whose label or value contains term, ignoring case.