View Source Phoenix.LiveDashboard.PageBuilder behaviour (LiveDashboard v0.8.5)
Page builder is the default mechanism for building custom dashboard pages.
Each dashboard page is a LiveView with additional callbacks for customizing the menu appearance and the automatic refresh.
A simple and straight-forward example of a custom page is the
Phoenix.LiveDashboard.EtsPage
that ships with the dashboard:
defmodule Phoenix.LiveDashboard.EtsPage do
@moduledoc false
use Phoenix.LiveDashboard.PageBuilder
@impl true
def menu_link(_, _) do
{:ok, "ETS"}
end
@impl true
def render(assigns) do
~H"""
<.live_table
id="ets-table"
dom_id="ets-table"
page={@page}
title="ETS"
row_fetcher={&fetch_ets/2}
row_attrs={&row_attrs/1}
rows_name="tables"
>
<:col field={:name} header="Name or module" />
<:col field={:protection} />
<:col field={:type} />
<:col field={:size} text_align="right" sortable={:desc} />
<:col field={:memory} text_align="right" sortable={:desc} :let={ets}>
<%= format_words(ets[:memory]) %>
</:col>
<:col field={:owner} :let={ets} >
<%= encode_pid(ets[:owner]) %>
</:col>
</.live_table>
"""
end
defp fetch_ets(params, node) do
%{search: search, sort_by: sort_by, sort_dir: sort_dir, limit: limit} = params
# Here goes the code that goes through all ETS tables, searches
# (if not nil), sorts, and limits them.
#
# It must return a tuple where the first element is list with
# the current entries (up to limit) and an integer with the
# total amount of entries.
# ...
end
defp row_attrs(table) do
[
{"phx-click", "show_info"},
{"phx-value-info", encode_ets(table[:id])},
{"phx-page-loading", true}
]
end
end
Once a page is defined, it must be declared in your live_dashboard
route as follows:
live_dashboard "/dashboard",
additional_pages: [
route_name: MyAppWeb.MyCustomPage
]
Or alternatively:
live_dashboard "/dashboard",
additional_pages: [
route_name: {MyAppWeb.MyCustomPage, some_option: ...}
]
The second argument of the tuple will be given to the init/1
callback. If not tuple is given, init/1
will receive an empty
list.
Options for the use macro
The following options can be given when using the PageBuilder
module:
refresher?
- Boolean to enable or disable the automatic refresh in the page.
Components
A page can return any valid HEEx template in the render/1
callback,
and it can use the components listed with this page too.
We currently support card/1
, fields_card/1
, row/1
,
shared_usage_card/1
, and usage_card/1
;
and the live components live_layered_graph/1
, live_nav_bar/1
,
and live_table/1
.
Helpers
Some helpers are available for page building. The supported
helpers are: live_dashboard_path/2
, live_dashboard_path/3
,
encode_app/1
, encode_ets/1
, encode_pid/1
, encode_port/1
,
and encode_socket/1
.
Custom Hooks
If your page needs to register custom hooks, you can use the register_after_opening_head_tag/2
function. Because the hooks need to be available on the dead render in the layout, before the
LiveView's LiveSocket is configured, your need to do this inside an on_mount
hook:
defmodule MyAppWeb.MyLiveDashboardHooks do
import Phoenix.LiveView
import Phoenix.Component
alias Phoenix.LiveDashboard.PageBuilder
def on_mount(:default, _params, _session, socket) do
{:cont, PageBuilder.register_after_opening_head_tag(socket, &after_opening_head_tag/1)}
end
defp after_opening_head_tag(assigns) do
~H"""
<script nonce={@csp_nonces[:script]}>
window.LiveDashboard.registerCustomHooks({
MyHook: {
mounted() {
// do something
}
}
})
</script>
"""
end
end
defmodule MyAppWeb.MyCustomPage do
...
end
And then add it to the list of on_mount
hooks in the live_dashboard
router configuration:
live_dashboard "/dashboard",
additional_pages: [
route_name: MyAppWeb.MyCustomPage
],
on_mount: [
MyAppWeb.MyLiveDashboardHooks
]
The LiveDashboard provides a function window.LiveDashboard.registerCustomHooks({ ... })
that you can call
with an object of hook declarations.
Note that in order to use external libraries, you will either need to include them from a CDN, or bundle them yourself and include them from your app's static paths.
A note on CSPs and libraries
Phoenix LiveDashboard supports CSP nonces for its own assets, configurable using the
Phoenix.LiveDashboard.Router.live_dashboard/2
macro by setting the:csp_nonce_assign_key
option. If you are building a library, ensure that you render those CSP nonces on any scripts, styles or images of your page. The nonces are passed to your custom page under the:csp_nonces
assign and also available in theafter_opening_head_tag
component.You should use those when including scripts or styles like this:
<script nonce={@csp_nonces[:script]}>...</script> <script nonce={@csp_nonces[:script]} src="..."></script> <style nonce={@csp_nonces[:style]}>...</style> <link rel="stylesheet" href="..." nonce={@csp_nonces[:style]}>
This ensures that your custom page can be used when a CSP is in place using the mechanism supported by Phoenix LiveDashboard.
If your custom page needs a different CSP policy, for example due to inline styles set by scripts, please consider documenting these requirements.
Summary
Components
Card component.
Card title component.
Fields card component.
Hint pop-up text component
List of label value.
A component for drawing layered graphs.
Nav bar live component.
Table live component.
Row component.
Shared usage card component.
Usage card component.
Callbacks
Callback invoked when an event is called.
Callback invoked when the automatic refresh is enabled.
Callback invoked when a page is declared in the router.
Callback invoked when a page is declared in the router.
Functions
Encodes an application for URLs.
Encodes ETSs references for URLs.
Encodes PIDs for URLs.
Encodes Port for URLs.
Encodes Sockets for URLs.
Computes a router path to the current page.
Computes a router path to the current page with merged params.
Registers a component to be rendered after the opening head tag in the layout.
Registers a component to be rendered before the closing head tag in the layout.
Components
@spec card(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Card component.
You can see it in use the Home and OS Data pages.
Attributes
title
(:string
) - The title above the card. Defaults tonil
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.inner_title
(:string
) - The title inside the card. Defaults tonil
.inner_hint
(:string
) - A textual hint to show close to the inner title. Defaults tonil
.dom_id
(:string
) - id attribute for the HTML the main tag. Defaults tonil
.
Slots
inner_block
(required) - The value that the card will show.
@spec card_title(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Card title component.
Attributes
title
(:string
) - The title above the card. Defaults tonil
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.
Fields card component.
You can see it in use the Home page in the Environment section.
Attributes
fields
(:list
) (required) - A list of key-value elements that will be shown inside the card.title
(:string
) - The title above the card. Defaults tonil
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.inner_title
(:string
) - The title inside the card. Defaults tonil
.inner_hint
(:string
) - A textual hint to show close to the inner title. Defaults tonil
.
@spec hint(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Hint pop-up text component
Attributes
text
(:string
) (required) - Text to show in the hint.
List of label value.
You can see it in use in the modal in Ports or Processes page.
Slots
elem
(required) - Value for each element of the list. Accepts attributes:label
(:string
) (required) - Label for the elem.
@spec live_layered_graph(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
A component for drawing layered graphs.
This is useful to represent pipelines like we have on BroadwayDashboard where each layer points to nodes of the layer below. It draws the layers from top to bottom.
The calculation of layers and positions is done automatically based on options.
Attributes
id
(:any
) (required) - Because is a statefulPhoenix.LiveComponent
an unique id is needed.title
(:string
) - The title of the component. Defaults tonil
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.layers
(:list
) (required) - A graph of layers with nodes. They represent our graph structure (see example). Each layer is a list of nodes, where each node has the following fields::id
- The ID of the given node.:children
- The IDs of children nodes.:data
- A string or a map. If it's a map, the required fields aredetail
andlabel
.
show_grid?
(:boolean
) - Enable or disable the display of a grid. This is useful for development. Defaults tofalse
.y_label_offset
(:integer
) - The "y" offset of label position relative to the center of its circle. Defaults to5
.y_detail_offset
(:integer
) - The "y" offset of detail position relative to the center of its circle. Defaults to18
.background
(:any
) - A function that calculates the background for a node based on it's data. Default:fn _node_data -> "gray" end
."format_label
(:any
) - A function that formats the label. Defaults to a function that returns the label or data if data is binary.format_detail
(:any
) - A function that formats the detail field. This is only going to be called if data is a map. Default:fn node_data -> node_data.detail end
.
Examples
iex> layers = [
...> [
...> %{
...> id: "a1",
...> data: "a1",
...> children: ["b1"]
...> }
...> ],
...> [
...> %{
...> id: "b1"
...> data: %{
...> detail: 0,
...> label: "b1"
...> },
...> children: []
...> }
...> ]
...> ]
@spec live_table(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Table live component.
You can see it in use the applications, processes, sockets pages and many others.
Attributes
id
(:any
) (required) - Because is a statefulPhoenix.LiveComponent
an unique id is needed.page
(Phoenix.LiveDashboard.PageBuilder
) (required) - Dashboard page.row_fetcher
(:any
) (required) - A function which receives the params and the node and returns a tuple with the rows and the total number:(params(), node() -> {list(), integer() | binary()})
. Optionally, if the function needs to keep a state, it can be defined as a tuple where the first element is a function and the second is the initial state. In this case, the function will receive the state as third argument and must return a tuple with the rows, the total number, and the new state for the following call:{(params(), node(), term() -> {list(), integer() | binary(), term()}), term()}
rows_name
(:string
) - A string to name the representation of the rows. Default is calculated from the current page.row_attrs
(:any
) - A list with the HTML attributes for the table row. It can be also a function that receive the row as argument and returns a list of 2 element tuple with HTML attribute name and value.Defaults to
nil
.default_sort_by
(:any
) - The default column to sort by to. Defaults to the first sortable column. Defaults tonil
.title
(:string
) (required) - The title of the table.limit
(:any
) - May be set tofalse
to disable thelimit
. Defaults to[50, 100, 500, 1000, 5000]
.search
(:boolean
) - A boolean indicating if the search functionality is enabled. Defaults totrue
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.dom_id
(:string
) - id attribute for the HTML the main tag. Defaults tonil
.
Slots
col
(required) - Columns for the table. Accepts attributes:field
(:atom
) (required) - Identifier for the column.sortable
(:atom
) - When set, the column header is clickable and it fetches again rows with the new order. Required for at least one column. Must be one of:asc
, or:desc
.header
(:string
) - Label to show in the current column. Default value is calculated from:field
.text_align
(:string
) - Text align for text in the column. Default:nil
.Must be one of"left"
,"center"
,"right"
, or"justify"
.
@spec row(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Row component.
You can see it in use the Home page and OS Data pages.
Slots
col
(required) - A list of components. It can receive up to 3 components. Each element will be one column.
@spec usage_card(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Usage card component.
You can see it in use the Home page and OS Data pages.
Attributes
title
(:string
) - The title above the card. Defaults tonil
.hint
(:string
) - A textual hint to show close to the title. Defaults tonil
.dom_id
(:string
) (required) - A unique identifier for all usages in this card.csp_nonces
(:any
) (required) - A copy of CSP nonces (@csp_nonces
) used to render the page safely.
Slots
usage
(required) - List of usages to show. Accepts attributes:current
(:integer
) (required) - The current value of the usage.limit
(:integer
) (required) - The max value of usage.dom_id
(:string
) (required) - An unique identifier for the usage that will be concatenated todom_id
.percent
(:string
) - The used percent of the usage.title
(:string
) - The title of the usage.hint
(:string
) - A textual hint to show close to the usage title.
Types
Callbacks
@callback handle_event(event :: binary(), unsigned_params(), socket :: Socket.t()) :: {:noreply, Socket.t()} | {:reply, map(), Socket.t()}
Callback invoked when an event is called.
Note that show_info
event is handled automatically by
Phoenix.LiveDashboard.PageBuilder
,
but the info
parameter (phx-value-info
) needs to be encoded with
one of the encode_*
helper functions.
For more details, see Phoenix.LiveView bindings
@callback handle_info(msg :: term(), socket :: Socket.t()) :: {:noreply, Socket.t()}
@callback handle_params(unsigned_params(), uri :: String.t(), socket :: Socket.t()) :: {:noreply, Socket.t()}
@callback handle_refresh(socket :: Socket.t()) :: {:noreply, Socket.t()}
Callback invoked when the automatic refresh is enabled.
@callback init(term()) :: {:ok, session()} | {:ok, session(), requirements()}
Callback invoked when a page is declared in the router.
It receives the router options and it must return the
tuple {:ok, session, requirements}
.
The page session will be serialized to the client and
received on mount
.
The requirements is an optional keyword to detect the state of the node.
The result of this detection will be passed as second
argument in the menu_link/2
callback.
The possible values are:
:applications
list of applications that are running or not.:modules
list of modules that are loaded or not.:pids
list of processes that alive or not.
@callback mount(unsigned_params(), session(), socket :: Socket.t()) :: {:ok, Socket.t()} | {:ok, Socket.t(), keyword()}
@callback render(assigns :: Socket.assigns()) :: Phoenix.LiveView.Rendered.t()
Functions
Encodes an application for URLs.
Example
This function can be used to encode an application for an event value:
<button phx-click="show-info" phx-value-info=<%= encode_app(@my_app) %>/>
Encodes ETSs references for URLs.
Example
This function can be used to encode an ETS reference for an event value:
<button phx-click="show-info" phx-value-info=<%= encode_ets(@reference) %>/>
Encodes PIDs for URLs.
Example
This function can be used to encode a PID for an event value:
<button phx-click="show-info" phx-value-info=<%= encode_pid(@pid) %>/>
Encodes Port for URLs.
Example
This function can be used to encode a Port for an event value:
<button phx-click="show-info" phx-value-info=<%= encode_port(@port) %>/>
Encodes Sockets for URLs.
Example
This function can be used to encode @socket
for an event value:
<button phx-click="show-info" phx-value-info=<%= encode_socket(@socket) %>/>
@spec live_dashboard_path( Socket.t(), page :: %Phoenix.LiveDashboard.PageBuilder{ allow_destructive_actions: term(), info: term(), module: term(), node: term(), params: term(), route: term(), tick: term() } ) :: binary()
Computes a router path to the current page.
@spec live_dashboard_path( Socket.t(), page :: %Phoenix.LiveDashboard.PageBuilder{ allow_destructive_actions: term(), info: term(), module: term(), node: term(), params: term(), route: term(), tick: term() }, map() | Keyword.t() ) :: binary()
Computes a router path to the current page with merged params.
Registers a component to be rendered after the opening head tag in the layout.
Registers a component to be rendered before the closing head tag in the layout.