Flop.Phoenix (Flop Phoenix v0.12.0) View Source
Components for Phoenix and Flop.
Introduction
Please refer to the Readme for an introduction.
Customization
The default classes, attributes, texts and symbols can be overridden by
passing the opts
assign. Since you probably will use the same opts
in all
your templates, you can globally configure an opts
provider function for
each component.
The functions have to return the options as a keyword list. The overrides are deep-merged into the default options.
defmodule MyAppWeb.ViewHelpers do
import Phoenix.HTML
def pagination_opts do
[
ellipsis_attrs: [class: "ellipsis"],
ellipsis_content: "‥",
next_link_attrs: [class: "next"],
next_link_content: next_icon(),
page_links: {:ellipsis, 7},
pagination_link_aria_label: &"#{&1}ページ目へ",
previous_link_attrs: [class: "prev"],
previous_link_content: previous_icon()
]
end
defp next_icon do
tag :i, class: "fas fa-chevron-right"
end
defp previous_icon do
tag :i, class: "fas fa-chevron-left"
end
def table_opts do
[
container: true,
container_attrs: [class: "table-container"],
no_results_content: content_tag(:p, do: "Nothing found."),
table_attrs: [class: "table"]
]
end
end
Refer to pagination_option/0
and table_option/0
for a list of
available options and defaults.
Once you have defined these functions, you can reference them with a
module/function tuple in config/config.exs
.
config :flop_phoenix,
pagination: [opts: {MyApp.ViewHelpers, :pagination_opts}],
table: [opts: {MyApp.ViewHelpers, :table_opts}]
Hiding default parameters
Default values for page size and ordering are omitted from the query
parameters. If you pass the :for
assign, the Flop.Phoenix function will
pick up the default values from the schema module deriving Flop.Schema
.
Links
Links are generated with Phoenix.LiveView.Helpers.live_patch/2
. This will
lead to <a>
tags with data-phx-link
and data-phx-link-state
attributes,
which will be ignored outside of LiveViews and LiveComponents.
When used within a LiveView or LiveComponent, you will need to handle the new
params in the Phoenix.LiveView.handle_params/3
callback of your LiveView
module.
Event-Based Pagination and Sorting
To make Flop.Phoenix
use event based pagination and sorting, you need to
assign the :event
to the pagination and table components. This will
generate an <a>
tag with phx-click
and phx-value
attributes set.
You can set a different target by assigning a :target
. The value
will be used as the phx-target
attribute.
<Flop.Phoenix.pagination
meta={@meta}
event="paginate-pets"
target={@myself}
/>
You will need to handle the event in the Phoenix.LiveView.handle_event/3
or Phoenix.LiveComponent.handle_event/3
callback of your
LiveView or LiveComponent module. The event name will be the one you set with
the :event
option.
def handle_event("paginate-pets", %{"page" => page}, socket) do
flop = Flop.set_page(socket.assigns.meta.flop, page)
with {:ok, {pets, meta}} <- Pets.list_pets(params) do
{:noreply, assign(socket, pets: pets, meta: meta)}
end
end
def handle_event("order_pets", %{"order" => order}, socket) do
flop = Flop.push_order(socket.assigns.meta.flop, order)
with {:ok, {pets, meta}} <- Pets.list_pets(flop) do
{:noreply, assign(socket, pets: pets, meta: meta)}
end
end
Link to this section Summary
Types
Defines the available options for Flop.Phoenix.pagination/1
.
Defines the available options for Flop.Phoenix.table/1
.
Components
Renders all inputs for a filter form including the hidden inputs.
Generates hidden inputs for the given form.
Renders an input for the :value
field and hidden inputs of a filter.
Renders a label for the :value
field of a filter.
Generates a pagination element.
Generates a table with sortable columns.
Miscellaneous
Builds a path that includes query parameters for the given Flop
struct
using the referenced Phoenix path helper function.
Converts a Flop struct into a keyword list that can be used as a query with Phoenix route helper functions.
Link to this section Types
Specs
pagination_option() :: {:current_link_attrs, keyword()} | {:ellipsis_attrs, keyword()} | {:ellipsis_content, Phoenix.HTML.safe() | binary()} | {:next_link_attrs, keyword()} | {:next_link_content, Phoenix.HTML.safe() | binary()} | {:page_links, :all | :hide | {:ellipsis, pos_integer()}} | {:pagination_link_aria_label, (pos_integer() -> binary())} | {:pagination_link_attrs, keyword()} | {:pagination_list_attrs, keyword()} | {:previous_link_attrs, keyword()} | {:previous_link_content, Phoenix.HTML.safe() | binary()} | {:wrapper_attrs, keyword()}
Defines the available options for Flop.Phoenix.pagination/1
.
:current_link_attrs
- The attributes for the link to the current page. Default:[class: "pagination-link is-current", aria: [current: "page"]]
.:ellipsis_attrs
- The attributes for the<span>
that wraps the ellipsis. Default:[class: "pagination-ellipsis"]
.:ellipsis_content
- The content for the ellipsis element. Default:{:safe, "…"}
.:next_link_attrs
- The attributes for the link to the next page. Default:[aria: [label: "Go to next page"], class: "pagination-next"]
.:next_link_content
- The content for the link to the next page. Default:"Next"
.:page_links
- Specifies how many page links should be rendered. Default::all
.:all
- Renders all page links.{:ellipsis, n}
- Rendersn
page links. Renders ellipsis elements if there are more pages than displayed.:hide
- Does not render any page links.
:pagination_link_aria_label
- 1-arity function that takes a page number and returns an aria label for the corresponding page link. Default:&"Go to page #{&1}"
.:pagination_link_attrs
- The attributes for the pagination links. Default:[class: "pagination-link"]
.:pagination_list_attrs
- The attributes for the pagination list. Default:[class: "pagination-list"]
.:previous_link_attrs
- The attributes for the link to the previous page. Default:[aria: [label: "Go to previous page"], class: "pagination-previous"]
.:previous_link_content
- The content for the link to the previous page. Default:"Previous"
.:wrappers_attrs
- The attributes for the<nav>
element that wraps the pagination links. Default:nil
.
Specs
table_option() :: {:container, boolean()} | {:container_attrs, keyword()} | {:no_results_content, Phoenix.HTML.safe() | binary()} | {:symbol_asc, Phoenix.HTML.safe() | binary()} | {:symbol_attrs, keyword()} | {:symbol_desc, Phoenix.HTML.safe() | binary()} | {:table_attrs, keyword()} | {:tbody_td_attrs, keyword()} | {:tbody_tr_attrs, keyword()} | {:th_wrapper_attrs, keyword()} | {:thead_th_attrs, keyword()} | {:thead_tr_attrs, keyword()}
Defines the available options for Flop.Phoenix.table/1
.
:container
- Wraps the table in a<div>
iftrue
. Default:false
.:container_attrs
- The attributes for the table container. Default:[class: "table-container"]
.:no_results_content
- Any content that should be rendered if there are no results. Default:<p>No results.</p>
.:table_attrs
- The attributes for the<table>
element. Default:[]
.:th_wrapper_attrs
- The attributes for the<span>
element that wraps the header link and the order direction symbol. Default:[]
.:symbol_asc
- The symbol that is used to indicate that the column is sorted in ascending order. Default:"▴"
.:symbol_attrs
- The attributes for the<span>
element that wraps the order direction indicator in the header columns. Default:[class: "order-direction"]
.:symbol_desc
- The symbol that is used to indicate that the column is sorted in ascending order. Default:"▾"
.:tbody_td_attrs
: Attributes to added to each<td>
tag within the<tbody>
. Default:[]
.:tbody_tr_attrs
: Attributes to added to each<tr>
tag within the<tbody>
. Default:[]
.:thead_th_attrs
: Attributes to added to each<th>
tag within the<thead>
. Default:[]
.:thead_tr_attrs
: Attributes to added to each<tr>
tag within the<thead>
. Default:[]
.
Link to this section Components
Specs
filter_fields(map()) :: Phoenix.LiveView.Rendered.t()
Renders all inputs for a filter form including the hidden inputs.
If you need more control, you can use filter_input/1
and filter_label/1
directly.
Example
<.form let={f} for={@meta}>
<.filter_fields let={entry} form={f} fields={[:email, :name]}>
<%= entry.label %>
<%= entry.input %>
</.filter_fields>
</.form>
Assigns
form
- ThePhoenix.HTML.Form
.fields
- The list of fields and field options. Note that inputs will not be rendered for fields that are not marked as filterable in the schema.dynamic
(optional) - Iftrue
, fields are only rendered for filters that are present in theFlop.Meta
struct passed to the form. You can use this for rendering filter forms that allow the user to add and remove filters dynamically. Thefields
assign is only used for looking up the options in that case. Defaults tofalse
.id
(optional) - Overrides the ID for the nested filter inputs.input_opts
(optional) - Additional options passed to each input.label_opts
(optional) - Additional options passed to each label.
Inner block
The generated labels and inputs are passed to the inner block instead of being automatically rendered. This allows you to customize the markup.
<.filter_fields let={e} form={f} fields={[:email, :name]}>
<div class="field-label"><%= e.label %></div>
<div class="field-body"><%= e.input %></div>
</.filter_fields>
Field configuration
The fields can be passed as atoms or keywords with additional options.
fields={[:name, :email]}
Or
fields={[
name: [label: gettext("Name")],
email: [
label: gettext("Email"),
op: :ilike_and,
type: :email_input
]
]}
Options:
label
op
type
default
The value under the :type
key matches the format used in filter_input/1
.
Any additional options will be passed to the input (e.g. HTML classes).
Label and input opts
You can set default attributes for all labels and inputs:
<.filter_fields
let={e}
form={f}
fields={[:name]}
input_opts={[class: "input"]}
label_opts={[class: "label"]}
>
The additional options in the type configuration are merged into the input opts. This means you can set a default class and override it for individual fields.
<.filter_fields
let={e}
form={f}
fields={[
:name,
:email,
role: [type: {:select, ["author", "editor"], class: "select"}]
]}
input_opts={[class: "input"]}
>
Specs
filter_input(map()) :: Phoenix.LiveView.Rendered.t()
Renders an input for the :value
field and hidden inputs of a filter.
This function must be used within the Phoenix.HTML.Form.inputs_for/2
,
Phoenix.HTML.Form.inputs_for/3
or Phoenix.HTML.Form.inputs_for/4
block of
the filter form.
Assigns
form
- The filter form.skip_hidden
(optional) - Disables the rendering of the hidden inputs for the filter. Default:false
.types
(optional) - Either a function or a keyword list that maps fields to input types
All additional assigns will be passed to the input function.
Example
<.form let={f} for={@meta}>
<%= filter_hidden_inputs_for(f) %>
<%= for ff <- inputs_for(f, :filters, fields: [:email]) do %>
<.filter_label form={ff} />
<.filter_input form={ff} />
<% end %>
</.form>
Types
By default, the input type is inferred from the field type in the Ecto schema. You can override the default type by passing a keyword list or a function that maps fields to types.
<.filter_input form={ff} types={[
email: :email_input,
phone: :telephone_input
]} />
Or
<.filter_input form={ff} types={
fn
:email -> :email_input
:phone -> :telephone_input
end
} />
The type can be given as:
- An atom referencing the input function from
Phoenix.HTML.Form
::telephone_input
- A tuple with an atom and additional options. The given list is merged into
the
opts
assign and passed to the input:{:telephone_input, class: "phone"}
- A tuple with an atom, options for a select input, and additional options:
{:select, ["Option a": "a", "Option B": "b"], class: "select"}
- A 3-arity function taking the form, field and opts. This is useful for
custom input functions:
fn form, field, opts -> ... end
or&my_custom_input/3
- A tuple with a 3-arity function and additional opts:
{&my_custom_input/3, class: "input"}
- A tuple with a 4-arity function, a list of options and additional opts:
{fn form, field, options, opts -> ... end, ["Option a": "a", "Option B": "b"], class: "select"}
Specs
filter_label(map()) :: Phoenix.LiveView.Rendered.t()
Renders a label for the :value
field of a filter.
This function must be used within the Phoenix.HTML.Form.inputs_for/2
,
Phoenix.HTML.Form.inputs_for/3
or Phoenix.HTML.Form.inputs_for/4
block of
the filter form.
Note that inputs_for
will not render inputs for fields that are not marked
as filterable in the schema, even if passed in the options.
Assigns
form
- The filter form.texts
(optional) - Either a function or a keyword list for setting the label text depending on the field.
All additional assigns will be passed to the label.
Example
<.form let={f} for={@meta}>
<%= filter_hidden_inputs_for(f) %>
<%= for ff <- inputs_for(f, :filters, fields: [:email]) do %>
<.filter_label form={ff} />
<.filter_input form={ff} />
<% end %>
</.form>
Flop.Phoenix.filter_hidden_inputs_for/1
is necessary because
Phoenix.HTML.Form.hidden_inputs_for/1
does not support lists in versions
<= 3.1.0.
Label text
By default, the label text is inferred from the value of the :field
key of
the filter. You can override the default type by passing a keyword list or a
function that maps fields to label texts.
<.filter_label form={ff} text={[
email: gettext("Email")
phone: gettext("Phone number")
]} />
Or
<.filter_label form={ff} text={
fn
:email -> gettext("Email")
:phone -> gettext("Phone number")
end
} />
Specs
pagination(map()) :: Phoenix.LiveView.Rendered.t()
Generates a pagination element.
Example
<Flop.Phoenix.pagination
meta={@meta}
path_helper={{Routes, :pet_path, [@socket, :index]}}
/>
Assigns
meta
- The meta information of the query as returned by theFlop
query functions.path_helper
- The path helper to use for building the link URL. Can be an mfa tuple or a function/args tuple. If set, links will be rendered withlive_patch/2
and the parameters have to be handled in thehandle_params/3
callback of the LiveView module.event
- If set,Flop.Phoenix
will render links with aphx-click
attribute.target
(optional) - Sets thephx-target
attribute for the pagination links.opts
(optional) - Options to customize the pagination. SeeFlop.Phoenix.pagination_option/0
. Note that the options passed to the function are deep merged into the default options. These options will likely be the same for all the tables in a project, so it probably makes sense to define them once in a function or set them in a wrapper function as described in theCustomization
section of the module documentation.
Hiding default parameters
If you pass the for
option to the Flop query function, Flop Phoenix hides
the order
and page_size
parameters if they match the default values defined
with Flop.Schema
.
Page link options
By default, page links for all pages are shown. You can limit the number of
page links or disable them altogether by passing the :page_links
option.
:all
: Show all page links (default).:hide
: Don't show any page links. Only the previous/next links will be shown.{:ellipsis, x}
: Limits the number of page links. The first and last page are always displayed. Thex
refers to the number of additional page links to show.
Pagination link aria label
For the page links, there is the :pagination_link_aria_label
option to set
the aria label. Since the page number is usually part of the aria label, you
need to pass a function that takes the page number as an integer and returns
the label as a string. The default is &"Goto page #{&1}"
.
Previous/next links
By default, the previous and next links contain the texts Previous
and
Next
. To change this, you can pass the :previous_link_content
and
:next_link_content
options.
Specs
table(map()) :: Phoenix.LiveView.Rendered.t()
Generates a table with sortable columns.
Example
<Flop.Phoenix.table
items={@pets}
meta={@meta}
path_helper={{Routes, :pet_path, [@socket, :index]}}
>
<:col let={pet} label="Name" field={:name}><%= pet.name %></:col>
<:col let={pet} label="Age" field={:age}><%= pet.age %></:col>
</Flop.Phoenix.table>
Assigns
items
- The list of items to be displayed in rows. This is the result list returned by the query.meta
- TheFlop.Meta
struct returned by the query function.path_helper
- The path helper to use for building the link URL. Can be an mfa tuple or a function/args tuple. If set, links will be rendered withlive_path/2
and the parameters have to be handled in thehandle_params/3
callback of the LiveView module.event
- If set,Flop.Phoenix
will render links with aphx-click
attribute.event
(optional) - If set,Flop.Phoenix
will render links with aphx-click
attribute.target
(optional) - Sets thephx-target
attribute for the header links.opts
(optional) - Keyword list with additional options (seeFlop.Phoenix.table_option/0
). Note that the options passed to the function are deep merged into the default options. These options will likely be the same for all the tables in a project, so it probably makes sense to define them once in a function or set them in a wrapper function as described in theCustomization
section of the module documentation.
Flop.Schema
If you pass the for
option when making the query with Flop, Flop Phoenix can
determine which table columns are sortable. It also hides the order
and
page_size
parameters if they match the default values defined with
Flop.Schema
.
Col slot
For each column to render, add one <:col>
element.
<:col let={pet} label="Name" field={:name}><%= pet.name %></:col>
label
- The content for the header column.field
(optional) - The field name for sorting.show
(optional) - Boolean value to conditionally show the column. Defaults totrue
.hide
(optional) - Boolean value to conditionally hide the column. Defaults tofalse
.
Foot slot
You can optionally add a foot
. The inner block will be rendered inside
a tfoot
element.
<Flop.Phoenix.table>
<:foot>
<tr><td>Total: <span class="total"><%= @total %></span></td></tr>
</:foot>
</Flop.Phoenix.table>
Link to this section Miscellaneous
build_path(tuple, meta_or_flop_or_params, opts \\ [])
View Source (since 0.6.0)Specs
build_path( {module(), atom(), [any()]} | {function(), [any()]}, Flop.Meta.t() | Flop.t() | keyword(), keyword() ) :: String.t()
Builds a path that includes query parameters for the given Flop
struct
using the referenced Phoenix path helper function.
The first argument can be either an MFA tuple (module, function name as atom, arguments) or a 2-tuple (function, arguments).
Default values for limit
, page_size
, order_by
and order_directions
are
omitted from the query parameters. To pick up the default parameters from a
schema module deriving Flop.Schema
, you need to pass the :for
option.
Examples
iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path(
...> {Flop.PhoenixTest, :route_helper, [%Plug.Conn{}, :pets]},
...> flop
...> )
"/pets?page_size=10&page=2"
iex> pet_path = fn _conn, :index, query ->
...> "/pets?" <> Plug.Conn.Query.encode(query)
...> end
iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path({pet_path, [%Plug.Conn{}, :index]}, flop)
"/pets?page_size=10&page=2"
We're defining fake path helpers for the scope of the doctests. In a real
Phoenix application, you would pass something like
{Routes, :pet_path, args}
or {&Routes.pet_path/3, args}
as the
first argument.
You can also pass a Flop.Meta
struct or a keyword list as the third
argument.
iex> pet_path = fn _conn, :index, query ->
...> "/pets?" <> Plug.Conn.Query.encode(query)
...> end
iex> flop = %Flop{page: 2, page_size: 10}
iex> meta = %Flop.Meta{flop: flop}
iex> build_path({pet_path, [%Plug.Conn{}, :index]}, meta)
"/pets?page_size=10&page=2"
iex> query_params = to_query(flop)
iex> build_path({pet_path, [%Plug.Conn{}, :index]}, query_params)
"/pets?page_size=10&page=2"
If the path helper takes additional path parameters, just add them to the second argument.
iex> user_pet_path = fn _conn, :index, id, query ->
...> "/users/#{id}/pets?" <> Plug.Conn.Query.encode(query)
...> end
iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path({user_pet_path, [%Plug.Conn{}, :index, 123]}, flop)
"/users/123/pets?page_size=10&page=2"
If the last path helper argument is a query parameter list, the Flop parameters are merged into it.
iex> pet_url = fn _conn, :index, query ->
...> "https://pets.flop/pets?" <> Plug.Conn.Query.encode(query)
...> end
iex> flop = %Flop{order_by: :name, order_directions: [:desc]}
iex> build_path({pet_url, [%Plug.Conn{}, :index, [user_id: 123]]}, flop)
"https://pets.flop/pets?user_id=123&order_directions[]=desc&order_by=name"
iex> build_path(
...> {pet_url,
...> [%Plug.Conn{}, :index, [category: "small", user_id: 123]]},
...> flop
...> )
"https://pets.flop/pets?category=small&user_id=123&order_directions[]=desc&order_by=name"
Converts a Flop struct into a keyword list that can be used as a query with Phoenix route helper functions.
Default limits and default order parameters set via the application
environment are omitted. You can pass the :for
option to pick up the
default options from a schema module deriving Flop.Schema
. You can also
pass default_limit
and default_order
as options directly. The function
uses Flop.get_option/2
internally to retrieve the default options.
Examples
iex> to_query(%Flop{})
[]
iex> f = %Flop{order_by: [:name, :age], order_directions: [:desc, :asc]}
iex> to_query(f)
[order_directions: [:desc, :asc], order_by: [:name, :age]]
iex> f |> to_query |> Plug.Conn.Query.encode()
"order_directions[]=desc&order_directions[]=asc&order_by[]=name&order_by[]=age"
iex> f = %Flop{page: 5, page_size: 20}
iex> to_query(f)
[page_size: 20, page: 5]
iex> f = %Flop{first: 20, after: "g3QAAAABZAAEbmFtZW0AAAAFQXBwbGU="}
iex> to_query(f)
[first: 20, after: "g3QAAAABZAAEbmFtZW0AAAAFQXBwbGU="]
iex> f = %Flop{
...> filters: [
...> %Flop.Filter{field: :name, op: :=~, value: "Mag"},
...> %Flop.Filter{field: :age, op: :>, value: 25}
...> ]
...> }
iex> to_query(f)
[
filters: %{
0 => %{field: :name, op: :=~, value: "Mag"},
1 => %{field: :age, op: :>, value: 25}
}
]
iex> f |> to_query() |> Plug.Conn.Query.encode()
"filters[0][field]=name&filters[0][op]=%3D~&filters[0][value]=Mag&filters[1][field]=age&filters[1][op]=%3E&filters[1][value]=25"
iex> f = %Flop{page: 5, page_size: 20}
iex> to_query(f, default_limit: 20)
[page: 5]