Flop Phoenix

CI Hex Coverage Status

Flop Phoenix is an Elixir library for filtering, ordering and pagination with Ecto, Phoenix and Flop.

Installation

Add flop_phoenix to your list of dependencies in the mix.exs of your Phoenix application:

def deps do
  [
    {:flop_phoenix, "~> 0.7.0"}
  ]
end

Follow the instructions in the Flop documentation to set up your business logic.

Usage

In your controller, pass the data and the Flop meta struct to your template:

defmodule MyAppWeb.PetController do
  use MyAppWeb, :controller

  alias Flop
  alias MyApp.Pets
  alias MyApp.Pets.Pet

  action_fallback MyAppWeb.FallbackController

  def index(conn, params) do
    with {:ok, {pets, meta}} <- Pets.list_pets(params) do
      render(conn, "index.html", meta: meta, pets: pets)
    end
  end
end

In my_app_web.ex, find the view_helpers/0 macro and import Flop.Phoenix:

defp view_helpers do
  quote do
    # Use all HTML functionality (forms, tags, etc)
    use Phoenix.HTML

    # Import basic rendering functionality (render, render_layout, etc)
    import Phoenix.View

+   import Flop.Phoenix

    import MyAppWeb.ErrorHelpers
    import MyAppWeb.Gettext
    alias MyAppWeb.Router.Helpers, as: Routes
  end
end

In your index template, you can now add a sortable table and pagination links:

<h1>Pets</h1>

<%= table(%{
      items: @pets,
      meta: @meta,
      path_helper: &Routes.pet_path/3,
      path_helper_args: [@conn, :index],
      headers: [{"Name", :name}, {"Age", :age}],
      row_func: fn pet, _opts -> [pet.name, pet.age] end,
      opts: [for: MyApp.Pet]
  })
%>

<%= pagination(@meta, &Routes.pet_path/3, [@conn, :index]) %>

The second argument of Flop.Phoenix.pagination/4 is the path helper function, and the third argument is a list of arguments for that path helper. If you want to add path parameters, you can do it like this:

<%= pagination(@meta, &Routes.owner_pet_path/4, [@conn, :index, @owner]) %>

This works the same as the path_helper and path_helper_args values of Flop.Phoenix.table/1.

To keep your template clean, it is recommended to define a table_headers/1 and table_row/2 function in your view. The opts are passed as a second argument to the row_func, so you can add any additional parameters you need.

The view module:

defmodule MyApp.PetView do
  def table_headers do
    [
      # {display value, schema field}
      {"Name", :name},
      {"Age", :age},
      ""
    ]
  end

  def table_row(%Pet{} = pet, opts) do
    conn = Keyword.fetch!(opts, :conn)

    [
      pet.name,
      pet.age,
      link "show", to: Routes.pet_path(conn, :show, pet)
    ]
  end
end

The template:

<%= table(%{
      items: @pets,
      meta: @meta,
      path_helper: &Routes.pet_path/3,
      path_helper_args: [@conn, :index],
      headers: table_headers(),
      row_func: &table_row/2,
      opts: [conn: @conn, for: Pet]
  })
%>

Customization

If you want to customize the pagination or table markup, you would probably want to do that once for all templates. To do that, create a new file views/flop_helpers.ex (or maybe views/component_helpers.ex).

defmodule MyAppWeb.FlopHelpers do
  use Phoenix.HTML

  def pagination(meta, route_helper, route_helper_args) do
    opts = [
      next_link_content: next_icon(),
      previous_link_content: previous_icon(),
      wrapper_attrs: [class: "paginator"]
    ]

    Flop.Phoenix.pagination(meta, route_helper, route_helper_args, opts)
  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
end

Change the import in my_app_web.ex:

defp view_helpers do
  quote do
    # ...

-   import Flop.Phoenix
+   import MyAppWeb.FlopHelpers

    # ...
  end
end

You can do it similarly for Flop.Phoenix.table/1

Refer to the Flop.Phoenix module documentation for more examples.