This guide adds a data grid to a Phoenix 1.8 app with LiveView 1.2 (a Phoenix 1.7 app with LiveView 1.1 works the same way). At the end, a LiveView shows its users in a grid that sorts, searches and scrolls, and then the orders of a table, read from the database a block at a time.
Your app does not run Node.js for Masu. The Hex package has the built
bundle, and your esbuild profile imports it from deps/.
1. Add the dependency
In mix.exs:
def deps do
[
{:masu, "~> 0.1"}
]
endThen run mix deps.get.
2. Run the installer
mix masu.install --dry-run # shows the changes as a diff
mix masu.install
The installer changes two files, and prints what it changed:
assets/js/app.js: it addsimport { Masu } from "masu"and putsMasuin thehooksof theLiveSocket.assets/css/app.css: it adds the Masu style sheet, and the Sumi theme import as a comment.
It does not add what a file already has, so you can run it again. When it
cannot find the new LiveSocket(...) call, or its hooks are not an
object or a variable, it changes nothing in app.js and prints the lines
to add.
3. Import the components
In the html_helpers of your web module (lib/my_app_web.ex):
import Masu.Components4. A grid of a list
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, users: Accounts.list_users())}
end
def render(assigns) do
~H"""
<.data_grid id="users" rows={@users} label="Users" searchable>
<:col field={:name} />
<:col field={:email} />
<:col field={:inserted_at} type={:datetime} label="Joined" />
</.data_grid>
"""
end
endThe grid shows the first rows at once, rendered on the server. A click on a header sorts by it, the search box finds rows by their text, and the arrow keys move between the cells.
5. A grid of a table
For a table of many rows, give the grid an Ecto query and the repo instead of the rows:
<.data_grid
id="orders"
source={from(o in Order, where: o.shop_id == ^@shop.id)}
repo={MyApp.Repo}
label="Orders"
sort={[{:placed_at, :desc}]}
>
<:col field={:number} width={120} />
<:col field={:customer} />
<:col :let={order} field={:status} type={:enum}>
<.badge status={order.status} />
</:col>
<:col field={:total} type={:decimal} />
<:col field={:placed_at} type={:datetime} label="Placed" />
</.data_grid>Now the database sorts, searches and counts, and the LiveView reads only
the rows the person scrolls to: a hundred at a time, with LIMIT and
OFFSET. See Sources for what the query may have, and the
indexes that make it fast.
6. Test it
import Masu.Test
test "lists the newest orders first", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/orders")
assert [%{"number" => "A-1042"} | _] = rows(view, "orders")
assert total(view |> search("orders", "acme"), "orders") == 3
endSee Testing.