Filters Cheatsheet
View SourceFilter Types Overview
Available Filters
| Type | Module | Use Case |
|---|---|---|
| Boolean | LiveTable.Boolean | Toggle on/off conditions |
| Range | LiveTable.Range | Numeric/date ranges |
| Select | LiveTable.Select | Dropdown selection |
| Transformer | LiveTable.Transformer | Custom query logic |
Boolean Filter
Basic Usage
def filters do
[
in_stock: Boolean.new(:quantity, "in_stock", %{
label: "In Stock Only",
condition: dynamic([p], p.quantity > 0)
})
]
endOptions
| Option | Type | Description |
|---|---|---|
label | string | Display label |
condition | dynamic | Ecto dynamic condition |
default | boolean | Default state |
Examples
# Active records
active: Boolean.new(:active, "active", %{
label: "Active Only",
condition: dynamic([r], r.active == true)
})
# Published items
published: Boolean.new(:published_at, "published", %{
label: "Published",
condition: dynamic([r], not is_nil(r.published_at))
})
# Recent items (last 7 days)
recent: Boolean.new(:inserted_at, "recent", %{
label: "Last 7 Days",
condition: dynamic([r],
r.inserted_at >= ago(7, "day"))
})Range Filter
Basic Usage
def filters do
[
price_range: Range.new(:price, "price_range", %{
type: :number,
label: "Price Range",
min: 0,
max: 1000,
step: 10
})
]
endOptions
| Option | Type | Description |
|---|---|---|
label | string | Display label |
type | atom | :number, :date, :datetime |
min | number/date | Minimum value |
max | number/date | Maximum value |
step | number | Increment step |
Examples
# Number range
price: Range.new(:price, "price", %{
type: :number,
label: "Price",
min: 0,
max: 10000,
step: 100
})
# Date range
created: Range.new(:inserted_at, "created", %{
type: :date,
label: "Created Date"
})
# Quantity range
stock: Range.new(:quantity, "stock", %{
type: :number,
label: "Stock Level",
min: 0,
max: 1000
})Select Filter
Basic Usage
def filters do
[
status: Select.new(:status, "status", %{
label: "Status",
options: [
%{label: "Active", value: ["active"]},
%{label: "Pending", value: ["pending"]},
%{label: "Archived", value: ["archived"]}
]
})
]
endOptions
| Option | Type | Description |
|---|---|---|
label | string | Display label |
options | list | Available choices |
multiple | boolean | Allow multi-select |
searchable | boolean | Enable search in dropdown |
Examples
# Single select
category: Select.new(:category_id, "category", %{
label: "Category",
options: [
%{label: "Electronics", value: [1]},
%{label: "Clothing", value: [2]},
%{label: "Books", value: [3]}
]
})
# Multiple select
tags: Select.new(:tag, "tags", %{
label: "Tags",
multiple: true,
options: [
%{label: "Featured", value: ["featured"]},
%{label: "Sale", value: ["sale"]},
%{label: "New", value: ["new"]}
]
})
# Dynamic options
status: Select.new(:status, "status", %{
label: "Status",
options: Enum.map(statuses(), fn s ->
%{label: String.capitalize(s), value: [s]}
end)
})For Joined Tables
# Use tuple for joined field
category: Select.new({:categories, :name}, "category", %{
label: "Category",
options: [
%{label: "Electronics", value: ["Electronics"]},
%{label: "Clothing", value: ["Clothing"]}
]
})Transformer
Basic Usage
def filters do
[
custom: Transformer.new("custom_filter", %{
query_transformer: &apply_custom_filter/2
})
]
end
defp apply_custom_filter(query, filter_data) do
case filter_data do
%{"field" => value} when value != "" ->
from q in query, where: q.field == ^value
_ ->
query
end
endFunction Signature
def my_transformer(query, filter_data) do
# query: Current Ecto query
# filter_data: Map from URL params
# Returns: Modified query
query
endOptions
| Option | Type | Description |
|---|---|---|
query_transformer | function/2 | Transform function |
Alternative: {Module, :function} tuple
Examples
# Join and aggregate
sales_filter: Transformer.new("sales", %{
query_transformer: &filter_by_sales/2
})
defp filter_by_sales(query, %{"min" => min})
when min != "" do
from p in query,
join: s in Sale, on: s.product_id == p.id,
group_by: p.id,
having: sum(s.amount) >= ^String.to_integer(min)
end
defp filter_by_sales(query, _), do: query
# Complex conditions
date_filter: Transformer.new("dates", %{
query_transformer: &filter_dates/2
})
defp filter_dates(query, data) do
query
|> maybe_filter_start(data["start"])
|> maybe_filter_end(data["end"])
end
defp maybe_filter_start(q, nil), do: q
defp maybe_filter_start(q, ""), do: q
defp maybe_filter_start(q, date) do
from r in q, where: r.date >= ^date
endFilter Patterns
Combining Filters
def filters do
[
# Boolean toggles
active: Boolean.new(:active, "active", %{
label: "Active Only",
condition: dynamic([r], r.active == true)
}),
# Range filters
price: Range.new(:price, "price", %{
type: :number,
label: "Price Range"
}),
# Select dropdown
category: Select.new(:category, "category", %{
label: "Category",
options: category_options()
}),
# Custom transformer
search: Transformer.new("advanced", %{
query_transformer: &advanced_search/2
})
]
endAccessing Filter State
# In template, check applied filters
@options["filters"]["status"]
# In transformer, access all filter data
defp my_transformer(query, filter_data) do
IO.inspect(filter_data) # Debug
query
endQuick Reference
Filter Field Syntax
| Syntax | Use Case |
|---|---|
:field | Simple schema field |
{:alias, :field} | Joined table field |
Common Gotchas
- Boolean
conditionmust be an Ectodynamic - Select
valuemust be a list:["value"]not"value" - Transformer must always return a query
- For joins, use
{:alias, :field}tuple