MishkaGervaz.Table.Web.Events.RelationFilterHandler behaviour (MishkaGervaz v0.0.1-alpha.2)

Copy Markdown View Source

Handles relation filter events for dynamic search, load more, and multi-select.

This module manages server-side state for relation filters that need:

  • Async search with database queries
  • Paginated option loading
  • Multi-select with label resolution

Events Handled

  • search - Search options by term (debounced)
  • load_more - Load next page of options
  • focus - Open dropdown and load initial options
  • close_dropdown - Close dropdown on click-away
  • toggle - Toggle selection in multi-select mode

Public Validation Functions

  • skip_relation_search_term?/2 - Check if a value should be skipped as a filter value
  • valid_relation_value?/1 - Check if a value is a valid relation filter value (UUID or nil)

Client-Side Alternative

Users with custom JS comboboxes (Tom Select, Headless UI, etc.) can bypass these events entirely and just use the standard "filter" event to submit final selected values. These events are for server-managed UI state.

Customization

You can create a custom RelationFilterHandler:

defmodule MyApp.CustomRelationFilterHandler do
  use MishkaGervaz.Table.Web.Events.RelationFilterHandler

  def handle("toggle", params, state, socket) do
    MyApp.Analytics.track("relation_filter_toggle", params)
    super("toggle", params, state, socket)
  end
end

Then configure it in your resource's DSL:

mishka_gervaz do
  table do
    events do
      relation_filter MyApp.CustomRelationFilterHandler
    end
  end
end

See MishkaGervaz.Table.Web.Events, MishkaGervaz.Table.Web.DataLoader.RelationLoader, MishkaGervaz.Table.Types.Filter.Relation, and the sibling handlers SanitizationHandler, RecordHandler, SelectionHandler, BulkActionHandler, HookRunner.

Summary

Functions

Checks if a relation filter value should be skipped (not treated as a filter value).

Checks if a value is a valid relation filter value for the given ID type.

Types

params()

@type params() :: map()

socket()

@type socket() :: Phoenix.LiveView.Socket.t()

state()

@type state() :: MishkaGervaz.Table.Web.State.t()

Callbacks

handle(action, params, state, socket)

@callback handle(
  action :: String.t(),
  params :: params(),
  state :: state(),
  socket :: socket()
) ::
  {:noreply, socket()}

Functions

skip_relation_search_term?(arg1, value)

@spec skip_relation_search_term?(map(), term()) :: boolean()

Checks if a relation filter value should be skipped (not treated as a filter value).

For relation filters with :search or :search_multi mode, this returns true if the value is not valid for the filter's ID type - meaning it's likely a search term that should not be used as the actual filter value.

The id_type is determined from the related resource's primary key type:

  • :uuid - Validates as UUID
  • :uuid_v7 - Validates as UUID (same format)
  • :integer - Validates as integer
  • :string - Any non-empty string is valid

Examples

iex> skip_relation_search_term?(%{type: :relation, mode: :search, id_type: :uuid}, "some search text")
true

iex> skip_relation_search_term?(%{type: :relation, mode: :search, id_type: :uuid}, "550e8400-e29b-41d4-a716-446655440000")
false

iex> skip_relation_search_term?(%{type: :relation, mode: :search, id_type: :integer}, "123")
false

iex> skip_relation_search_term?(%{type: :text}, "any value")
false

valid_relation_value?(value, id_type)

@spec valid_relation_value?(term(), atom() | nil) :: boolean()

Checks if a value is a valid relation filter value for the given ID type.

ID Types

  • :uuid / :uuid_v7 - Must be a valid UUID string
  • :integer - Must be a valid integer string
  • :string - Any non-empty string is valid

Special Values

  • "__nil__" - Always valid, indicates "no selection" or "null"

Examples

iex> valid_relation_value?("__nil__", :uuid)
true

iex> valid_relation_value?("550e8400-e29b-41d4-a716-446655440000", :uuid)
true

iex> valid_relation_value?("123", :integer)
true

iex> valid_relation_value?("search text", :uuid)
false

iex> valid_relation_value?("any-string", :string)
true