View Source Flop.Phoenix (Flop Phoenix v0.15.0)

Phoenix components for pagination, sortable tables and filter forms with Flop.

introduction

Introduction

Please refer to the Readme for an introduction.

customization

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

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 are generated with Phoenix.Components.link/1. 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

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(flop) 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 a cursor pagination element.

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.

Removes the first filter for the given field in the Flop.t struct or keyword list and returns the filter value and the updated struct or keyword list.

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

Link to this type

cursor_pagination_option()

View Source
@type cursor_pagination_option() ::
  {:disabled_class, String.t()}
  | {:next_link_attrs, keyword()}
  | {:next_link_content, Phoenix.HTML.safe() | binary()}
  | {:previous_link_attrs, keyword()}
  | {:previous_link_content, Phoenix.HTML.safe() | binary()}
  | {:wrapper_attrs, keyword()}

Defines the available options for Flop.Phoenix.cursor_pagination/1.

  • :disabled - The class which is added to disabled links. Default: "disabled".
  • :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".
  • :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: [class: "pagination", role: "navigation", aria: [label: "pagination"]].
@type pagination_option() ::
  {:current_link_attrs, keyword()}
  | {:disabled_class, String.t()}
  | {: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"]].
  • :disabled - The class which is added to disabled links. Default: "disabled".
  • :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, "&hellip;"}.
  • :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} - Renders n 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.
@type 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()}
  | {:symbol_unsorted, 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> if true. 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: "▾".
  • :symbol_unsorted - The symbol that is used to indicate that the column is not sorted. Default: nil.
  • :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

Link to this function

cursor_pagination(assigns)

View Source
@spec cursor_pagination(map()) :: Phoenix.LiveView.Rendered.t()

Renders a cursor pagination element.

example

Example

<Flop.Phoenix.cursor_pagination
  meta={@meta}
  path={{Routes, :pet_path, [@socket, :index]}}
/>

handling-parameters-and-events

Handling parameters and events

If you set the path assign, a link with query parameters is rendered. In a LiveView, you need to handle the parameters in the Phoenix.LiveView.handle_params/3 callback.

def handle_params(params, _, socket) do
  {pets, meta} = MyApp.list_pets(params)
  {:noreply, assign(socket, meta: meta, pets: pets)}
end

If you use LiveView and set the event assign, you need to update the Flop parameters in the handle_event/3 callback.

def handle_event("paginate-users", %{"to" => to}, socket) do
  flop = Flop.set_cursor(socket.assigns.meta, to)
  {pets, meta} = MyApp.list_pets(flop)
  {:noreply, assign(socket, meta: meta, pets: pets)}
end

getting-the-right-parameters-from-flop

Getting the right parameters from Flop

This component requires the start and end cursors to be set in Flop.Meta. If you pass a Flop.Meta struct with page or offset-based parameters, this will result in an error. You can enforce cursor-based pagination in your query function with the default_pagination_type and pagination_types options.

def list_pets(params) do
  Flop.validate_and_run!(Pet, params,
    for: Pet,
    default_pagination_type: :first,
    pagination_types: [:first, :last]
  )
end

default_pagination_type ensures that Flop defaults to the right pagination type when it cannot determine the type from the parameters. pagination_types ensures that parameters for other types are not accepted.

order-fields

Order fields

The pagination cursor is based on the ORDER BY fields of the query. It is important that the combination of order fields is unique across the data set. You can use:

  • the field with the primary key
  • a field with a unique index
  • all fields of a composite primary key or unique index

If you want to order by fields that are not unique, you can add the primary key as the last order field. For example, if you want to order by family name and given name, you should set the order_by parameter to [:family_name, :given_name, :id].

attributes

Attributes

  • meta (Flop.Meta) (required) - The meta information of the query as returned by the Flop query functions.

  • path (:any) - Either a URI string (Phoenix verified route), an MFA or FA tuple (Phoenix route helper), or a 1-ary path builder function. See Flop.Phoenix.build_path/3 for details. If set, links will be rendered with Phoenix.Components.link/1 with the patch attribute. In a LiveView, the parameters will have to be handled in the handle_params/3 callback of the LiveView module. Alternatively, set :event, if you don't want the parameters to appear in the URL.

    Defaults to nil.

  • path_helper (:any) - Deprecated. Use :path instead. Defaults to nil.

  • event (:string) - If set, Flop.Phoenix will render links with a phx-click attribute. Alternatively, set :path, if you want the parameters to appear in the URL.

    Defaults to nil.

  • target (:string) - Sets the phx-target attribute for the pagination links. Defaults to nil.

  • reverse (:boolean) - By default, the next link moves forward with the :after parameter set to the end cursor, and the previous link moves backward with the :before parameter set to the start cursor. If reverse is set to true, the destinations of the links are switched.

    Defaults to false.

  • opts (:list) - Options to customize the pagination. See Flop.Phoenix.cursor_pagination_option/0. Note that the options passed to the function are deep merged into the default options. Since these options will likely be the same for all the cursor pagination links in a project, it is recommended to define them once in a function or set them in a wrapper function as described in the Customization section of the module documentation.

    Defaults to [].

Link to this function

filter_fields(assigns)

View Source (since 0.12.0)
@spec 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

Example

<.form :let={f} for={@meta}>
  <.filter_fields :let={entry} form={f} fields={[:email, :name]}>
    <%= entry.label %>
    <%= entry.input %>
  </.filter_fields>
</.form>

field-configuration

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 function (e.g. HTML classes or a list of options).

label-and-input-opts

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", phx_debounce: 100]}
  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"]}
>

attributes

Attributes

  • form (Phoenix.HTML.Form) (required)

  • fields (:list) - 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 (see Flop.Schema).

    If dynamic is set to false, only fields in this list are rendered. If dynamic is set to true, only fields for filters present in the given Flop.Meta struct are rendered, and the fields are rendered even if they are not passed in the fields list. In the latter case, fields is optional, but you can still pass label and input configuration this way.

    Note that in a dynamic form, it is not possible to configure a single field multiple times.

    Defaults to [].

  • dynamic (:boolean) - If true, fields are only rendered for filters that are present in the Flop.Meta struct passed to the form. You can use this for rendering filter forms that allow the user to add and remove filters dynamically. The fields assign is only used for looking up the options in that case.

    Defaults to false.

  • id (:string) - Overrides the ID for the nested filter inputs. Defaults to nil.

  • input_opts (:list) - Additional options passed to each input. Defaults to [].

  • label_opts (:list) - Additional options passed to each label. Defaults to [].

slots

Slots

  • 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>
Link to this function

filter_hidden_inputs_for(arg)

View Source (since 0.12.0)
@spec filter_hidden_inputs_for(Phoenix.HTML.Form.t()) :: [Phoenix.HTML.safe()]

Generates hidden inputs for the given form.

This does the same as Phoenix.HTML.Form.hidden_inputs_for/1 in versions <= 3.1.0, except that it supports list fields. If you use a later Phoenix.HTML version, you don't need this function.

Link to this function

filter_input(assigns)

View Source (since 0.12.0)
@spec 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.

example

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

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"}

attributes

Attributes

  • form (Phoenix.HTML.Form) (required)
  • skip_hidden (:boolean) - Disables the rendering of the hidden inputs for the filter. Defaults to false.
  • types (:any) - Either a function or a keyword list that maps fields to input types. Defaults to nil.
  • input_opts (:any) - Additional options to be passed to the input function. Defaults to [].
Link to this function

filter_label(assigns)

View Source (since 0.12.0)
@spec 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.

example

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

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
} />

attributes

Attributes

  • form (Phoenix.HTML.Form) (required)

  • texts (:any) - Either a function or a keyword list for setting the label text depending on the field.

    Defaults to nil.

  • rest (:global) - Additional attributes to be added to the <label>.

@spec pagination(map()) :: Phoenix.LiveView.Rendered.t()

Generates a pagination element.

examples

Examples

<Flop.Phoenix.pagination
  meta={@meta}
  path={~p"/pets"}
/>

<Flop.Phoenix.pagination
  meta={@meta}
  path={{Routes, :pet_path, [@socket, :index]}}
/>

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. The x refers to the number of additional page links to show.

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}".

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.

attributes

Attributes

  • meta (Flop.Meta) (required) - The meta information of the query as returned by the Flop query functions.

  • path (:any) - Either a URI string (Phoenix verified route), an MFA or FA tuple (Phoenix route helper), or a 1-ary path builder function. See Flop.Phoenix.build_path/3 for details. If set, links will be rendered with Phoenix.Components.link/1 with the patch attribute. In a LiveView, the parameters will have to be handled in the handle_params/3 callback of the LiveView module. Alternatively, set :event, if you don't want the parameters to appear in the URL.

    Defaults to nil.

  • path_helper (:any) - Deprecated. Use :path instead. Defaults to nil.

  • event (:string) - If set, Flop.Phoenix will render links with a phx-click attribute. Alternatively, set :path, if you want the parameters to appear in the URL.

    Defaults to nil.

  • target (:string) - Sets the phx-target attribute for the pagination links. Defaults to nil.

  • opts (:list) - Options to customize the pagination. See Flop.Phoenix.pagination_option/0. Note that the options passed to the function are deep merged into the default options. Since these options will likely be the same for all the tables in a project, it is recommended to define them once in a function or set them in a wrapper function as described in the Customization section of the module documentation.

    Defaults to [].

Link to this function

table(assigns)

View Source (since 0.6.0)
@spec table(map()) :: Phoenix.LiveView.Rendered.t()

Generates a table with sortable columns.

example

Example

<Flop.Phoenix.table
  items={@pets}
  meta={@meta}
  path={{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>

flop-schema

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.

attributes

Attributes

  • items (:list) (required) - The list of items to be displayed in rows. This is the result list returned by the query.

  • meta (Flop.Meta) (required) - The Flop.Meta struct returned by the query function.

  • path (:any) - Either a URI string (Phoenix verified route), an MFA or FA tuple (Phoenix route helper), or a 1-ary path builder function. See Flop.Phoenix.build_path/3 for details. If set, links will be rendered with Phoenix.Components.link/1 with the patch attribute. In a LiveView, the parameters will have to be handled in the handle_params/3 callback of the LiveView module. Alternatively, set :event, if you don't want the parameters to appear in the URL.

    Defaults to nil.

  • path_helper (:any) - Deprecated. Use :path instead. Defaults to nil.

  • event (:string) - If set, Flop.Phoenix will render links with a phx-click attribute. Alternatively, set :path, if you want the parameters to appear in the URL.

    Defaults to nil.

  • target (:string) - Sets the phx-target attribute for the header links. Defaults to nil.

  • caption (:string) - Content for the <caption> element. Defaults to nil.

  • opts (:list) - Keyword list with additional options (see Flop.Phoenix.table_option/0). Note that the options passed to the function are deep merged into the default options. Since these options will likely be the same for all the tables in a project, it is recommended to define them once in a function or set them in a wrapper function as described in the Customization section of the module documentation.

    Defaults to [].

slots

Slots

  • col (required) - For each column to render, add one <:col> element.

    <:col :let={pet} label="Name" field={:name} col_style="width: 20%;">
      <%= pet.name %>
    </:col>

    Any additional assigns will be added as attributes to the <td> elements.

    Accepts attributes:

    • label (:string) - The content for the header column.

    • field (:atom) - The field name for sorting.

    • show (:boolean) - Boolean value to conditionally show the column. Defaults to true.

    • hide (:boolean) - Boolean value to conditionally hide the column. Defaults to false.

    • col_style (:string) - If set, a <colgroup> element is rendered and the value of the col_style assign is set as style attribute for the <col> element of the respective column. You can set the width, background and border of a column this way.

    • rest (:global) - Any additional attributes to pass to the <td>.

  • foot - 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

Link to this function

build_path(path, meta_or_flop_or_params, opts \\ [])

View Source (since 0.6.0)
@spec build_path(
  String.t() | {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 one of:

  • an MFA tuple (module, function name as atom, arguments)
  • a 2-tuple (function, arguments)
  • a URL string (e.g. "/some/path"; this option has been added so that you can use Phoenix verified routes with the library)
  • a function that takes the Flop parameters as a keyword list as an argument

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

Examples

with-an-mfa-tuple

With an MFA tuple

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"

with-a-function-arguments-tuple

With a function/arguments tuple

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.

passing-a-flop-meta-struct-or-a-keyword-list

Passing a Flop.Meta struct or a keyword list

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"

additional-path-parameters

Additional path parameters

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"

additional-query-parameters

Additional query parameters

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"

with-a-uri-string-or-verified-route

With a URI string or verified route

You can also use this function with a verified route. Note that this example uses a plain string which isn't verified, because we need the doctest to work, and flop_phoenix does not depend on Phoenix 1.7. In a real application with Phoenix 1.7, you would use the p sigil instead (~p"/pets").

iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path("/pets", flop)
"/pets?page=2&page_size=10"

The Flop query parameters will be merged into existing query parameters.

iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path("/pets?species=dogs", flop)
"/pets?page=2&page_size=10&species=dogs"

set-page-as-path-parameter

Set page as path parameter

Finally, you can also pass a function that takes the Flop parameters as a keyword list as an argument. Default values will not be included in the parameters passed to the function. You can use this if you need to set some of the parameters as path parameters instead of query parameters.

iex> flop = %Flop{page: 2, page_size: 10}
iex> build_path(
...>   fn params ->
...>     {page, params} = Keyword.pop(params, :page)
...>     query = Plug.Conn.Query.encode(params)
...>     if page, do: "/pets/page/#{page}?#{query}", else: "/pets?#{query}"
...>   end,
...>   flop
...> )
"/pets/page/2?page_size=10"

Note that in this example, the anonymous function just returns a string. With Phoenix 1.7, you will be able to use verified routes.

build_path(
  fn params ->
    {page, query} = Keyword.pop(params, :page)
    if page, do: ~p"/pets/page/#{page}?#{query}", else: ~p"/pets?#{query}"
  end,
  flop
)

Note that the keyword list passed to the path builder function is built using Plug.Conn.Query.encode/2, which means filters are formatted as map with integer keys.

set-filter-value-as-path-parameter

Set filter value as path parameter

If you need to set a filter value as a path parameter, you can use Flop.Phoenix.pop_filter/2 to manipulate the parameters (again, replace the plain strings with verified routes and remove the encode line in Phoenix 1.7).

iex> flop = %Flop{
...>   page: 5,
...>   order_by: [:published_at],
...>   filters: [
...>     %Flop.Filter{field: :category, op: :==, value: "announcements"}
...>   ]
...> }
iex> build_path(
...>   fn params ->
...>     {page, params} = Keyword.pop(params, :page)
...>     {category, params} = pop_filter(params, :category)
...>     query = Plug.Conn.Query.encode(params)
...>
...>     case {page, category} do
...>       {nil, nil} -> "/articles?#{query}"
...>       {page, nil} -> "/articles/page/#{page}?#{query}"
...>       {nil, %{value: category}} -> "/articles/category/#{category}?#{query}"
...>       {page, %{value: category}} -> "/articles/category/#{category}/page/#{page}?#{query}"
...>     end
...>   end,
...>   flop
...> )
"/articles/category/announcements/page/5?order_by[]=published_at"
Link to this function

pop_filter(flop, field)

View Source (since 0.15.0)
@spec pop_filter(Flop.t(), atom()) :: {any(), Flop.t()}
@spec pop_filter(
  keyword(),
  atom()
) :: {any(), keyword()}

Removes the first filter for the given field in the Flop.t struct or keyword list and returns the filter value and the updated struct or keyword list.

If a keyword list is passed, it is expected to have the same format as returned by Flop.Phoenix.to_query/2.

You can use this function to write a custom path builder function in cases where you need to set a filter value as a path parameter instead of a query parameter. See Flop.Phoenix.build_path/3 for an example.

examples

Examples

with-a-flop-struct

With a Flop struct

iex> flop = %Flop{
...>   page: 5,
...>   filters: [
...>     %Flop.Filter{field: :category, op: :==, value: "announcements"},
...>     %Flop.Filter{field: :title, op: :==, value: "geo"}
...>   ]
...> }
iex> pop_filter(flop, :category)
{%Flop.Filter{field: :category, op: :==, value: "announcements"},
 %Flop{
   page: 5,
   filters: [%Flop.Filter{field: :title, op: :==, value: "geo"}]
 }}
iex> pop_filter(flop, :author)
{nil,
 %Flop{
   page: 5,
   filters: [
     %Flop.Filter{field: :category, op: :==, value: "announcements"},
     %Flop.Filter{field: :title, op: :==, value: "geo"}
   ]
 }
}

with-a-keyword-list

With a keyword list

iex> params = [
...>   filters: %{
...>     0 => %{field: :category, op: :==, value: "announcements"},
...>     1 => %{field: :title, op: :==, value: "geo"}
...>   },
...>   page: 5
...> ]
iex> pop_filter(params, :category)
{%{field: :category, op: :==, value: "announcements"},
 [
   filters: %{0 => %{field: :title, op: :==, value: "geo"}},
   page: 5
 ]}
iex> pop_filter(params, :author)
{nil,
 [
   filters: %{
     0 => %{field: :category, op: :==, value: "announcements"},
     1 => %{field: :title, op: :==, value: "geo"}
   },
   page: 5
 ]}

iex> pop_filter([], :category)
{nil, []}
Link to this function

to_query(flop, opts \\ [])

View Source (since 0.6.0)

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

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]