View Source Inertia.Controller (Inertia v3.0.0-rc3)
Controller functions for rendering Inertia.js responses.
Summary
Functions
Assigns errors to the Inertia page data. This helper accepts any data that
implements the Inertia.Errors protocol. By default, this library implements
error serializers for Ecto.Changeset and bare maps.
Assigns a prop value to the Inertia page data.
Assigns a shared prop value to the Inertia page data.
Enable (or disable) automatic conversion of prop keys from snake case (e.g.
inserted_at), which is conventional in Elixir, to camel case (e.g.
insertedAt), which is conventional in JavaScript.
Instruct the client-side to clear the history.
Instruct the client-side to encrypt history for this page.
Forces the Inertia.js client side to perform a redirect. This can be used as a plug or inline when building a response.
Marks a prop value as "always included", which means it will be included in the props on initial page load and subsequent partial loads (even when it's not explicitly requested).
Marks that a prop should be deeply merged with existing data on the client-side.
Marks that a prop should fetched immediately after the page is loaded on the client-side.
Marks that a prop should be merged with existing data on the client-side.
Marks a prop as a "once" prop, which is cached on the client-side and reused on subsequent pages that include the same prop.
Marks a prop value as optional, which means it will only get evaluated if explicitly requested in a partial reload.
Marks that a prop should be prepended (instead of appended) when merging with existing data on the client-side.
Determines if a response has been rendered with Inertia.
Marks a prop for infinite scroll pagination. Automatically configures merge behavior for the data key and extracts pagination metadata.
Marks a prop value as shared, causing its key to appear in the sharedProps page metadata.
Prevents auto-transformation of a prop key to camel-case (when
camelize_props is enabled).
Instruct the client-side to preserve the URL fragment across this navigation.
Renders an Inertia response.
Types
@opaque always()
@opaque deep_merge()
@opaque defer()
@opaque merge()
@opaque once()
@opaque optional()
@opaque prepend()
@opaque preserved_prop_key()
@type prop_key() :: raw_prop_key() | preserved_prop_key()
@type render_opt() :: {:ssr, boolean()}
@type render_opts() :: [render_opt()]
@opaque scroll()
Functions
@spec assign_errors(Plug.Conn.t(), data :: term()) :: Plug.Conn.t()
Assigns errors to the Inertia page data. This helper accepts any data that
implements the Inertia.Errors protocol. By default, this library implements
error serializers for Ecto.Changeset and bare maps.
If you are serializing your own errors maps, they should take the following shape:
%{
"name" => "Name is required",
"password" => "Password must be at least 5 characters",
"team.name" => "Team name is required",
}When assigning a changeset, you may optionally pass a message-generating function
to use when traversing errors. See Ecto.Changeset.traverse_errors/2
for more information about the message function.
defp default_msg_func({msg, opts}) do
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
end)
endThis default implementation performs a simple string replacement for error
message containing variables, like count. For example, given the following
error:
{"should be at least %{count} characters", [count: 3, validation: :length, min: 3]}The generated description would be "should be at least 3 characters". If you would
prefer to use the Gettext module for pluralizing and localizing error messages, you
can override the message function:
conn
|> assign_errors(changeset, fn {msg, opts} ->
if count = opts[:count] do
Gettext.dngettext(MyAppWeb.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(MyAppWeb.Gettext, "errors", msg, opts)
end
end)
@spec assign_errors(Plug.Conn.t(), data :: term(), msg_func :: function()) :: Plug.Conn.t()
@spec assign_prop(Plug.Conn.t(), prop_key(), any()) :: Plug.Conn.t()
Assigns a prop value to the Inertia page data.
@spec camelize_props(Plug.Conn.t()) :: Plug.Conn.t()
Enable (or disable) automatic conversion of prop keys from snake case (e.g.
inserted_at), which is conventional in Elixir, to camel case (e.g.
insertedAt), which is conventional in JavaScript.
Examples
Using camelize_props here will convert first_name to firstName in the
response props.
conn
|> assign_prop(:first_name, "Bob")
|> camelize_props()
|> render_inertia("Home")You may also pass a boolean to the camelize_props function (to override any
previously-set or globally-configured value):
conn
|> assign_prop(:first_name, "Bob")
|> camelize_props(false)
|> render_inertia("Home")
@spec camelize_props(Plug.Conn.t(), boolean()) :: Plug.Conn.t()
@spec clear_history(Plug.Conn.t()) :: Plug.Conn.t()
Instruct the client-side to clear the history.
@spec clear_history(Plug.Conn.t(), boolean()) :: Plug.Conn.t()
@spec encrypt_history(Plug.Conn.t()) :: Plug.Conn.t()
Instruct the client-side to encrypt history for this page.
@spec encrypt_history(Plug.Conn.t(), boolean()) :: Plug.Conn.t()
@spec force_inertia_redirect(Plug.Conn.t(), opts :: keyword()) :: Plug.Conn.t()
Forces the Inertia.js client side to perform a redirect. This can be used as a plug or inline when building a response.
This plug modifies the response to be a 409 Conflict response and include the
destination URL in the x-inertia-location header, which will cause the
Inertia client to perform a window.location = url visit.
Note: we automatically convert regular external redirects (via the Phoenix
redirect helper), but this function is useful if you need to force redirect
to a non-external route that is not handled by Inertia.
See https://inertiajs.com/redirects#external-redirects
Examples
conn
|> force_inertia_redirect()
|> redirect(to: "/non-inertia-powered-page")
Marks a prop value as "always included", which means it will be included in the props on initial page load and subsequent partial loads (even when it's not explicitly requested).
@spec inertia_deep_merge(value :: any(), opts :: keyword()) :: deep_merge()
Marks that a prop should be deeply merged with existing data on the client-side.
Options
:match_on- A key (or list of keys) used for client-side deduplication of merged items. Each key may be a dot-path into the merged items (e.g."data.id").
Marks that a prop should fetched immediately after the page is loaded on the client-side.
Options
:on_error- Controls what happens when the prop's resolver fails while the client is fetching its deferred group. Defaults to letting the error propagate (failing the partial reload). Pass:ignoreto degrade gracefully: the error is contained, the prop is omitted from the response, and its path is reported in therescuedPropspage metadata so the client can render a fallback. Rescued failures emit a[:inertia, :deferred_prop, :rescue]telemetry event and are logged.
Examples
# Default group
assign_prop(conn, :stats, inertia_defer(fn -> expensive_stats() end))
# Custom group
assign_prop(conn, :stats, inertia_defer(fn -> expensive_stats() end, "dashboard"))
# Degrade gracefully if the resolver fails
assign_prop(conn, :stats, inertia_defer(fn -> expensive_stats() end, on_error: :ignore))
# Custom group + graceful failure
assign_prop(conn, :stats, inertia_defer(fn -> expensive_stats() end, "dashboard", on_error: :ignore))
Marks that a prop should be merged with existing data on the client-side.
Options
:match_on- A key (or list of keys) used for client-side deduplication of merged items. Each key may be a dot-path into the merged items (e.g."data.id").
@spec inertia_once( fun_or_tagged :: fun() | defer() | merge() | prepend() | deep_merge() | optional(), opts :: keyword() ) :: once()
Marks a prop as a "once" prop, which is cached on the client-side and reused on subsequent pages that include the same prop.
Options
:fresh- Whentrue, forces the prop to be refreshed ignoring the client cache. Also accepts a boolean condition. Defaults tofalse.:until- Sets an expiration time. Accepts aDateTimeor integer seconds from now.:as- Custom key for sharing data across pages with different prop names.
Examples
# Basic once prop
assign_prop(conn, :plans, inertia_once(fn -> Plan.all() end))
# Force refresh
assign_prop(conn, :plans, inertia_once(fn -> Plan.all() end, fresh: true))
# With expiration (1 hour)
assign_prop(conn, :rates, inertia_once(fn -> ExchangeRate.all() end, until: 3600))
# With custom key for sharing across pages
assign_prop(conn, :member_roles, inertia_once(fn -> Role.all() end, as: "roles"))
# Combined options
assign_prop(conn, :plans, inertia_once(fn -> Plan.all() end,
fresh: should_refresh?,
until: DateTime.utc_now() |> DateTime.add(1, :day),
as: "billing_plans"
))
# Combining with other prop types
assign_prop(conn, :permissions, inertia_once(inertia_defer(fn -> Permission.all() end)))
Marks a prop value as optional, which means it will only get evaluated if explicitly requested in a partial reload.
Optional props will only be included the when explicitly requested in a partial reload. If you want to include the prop on first visit, you'll want to use a bare anonymous function or named function reference instead.
conn
# ALWAYS included on first visit...
# OPTIONALLY included on partial reloads...
# ALWAYS evaluated...
|> assign_prop(:cheap_thing, cheap_thing())
# ALWAYS included on first visit...
# OPTIONALLY included on partial reloads...
# ONLY evaluated when needed...
|> assign_prop(:expensive_thing, fn -> calculate_thing() end)
|> assign_prop(:another_expensive_thing, &calculate_another_thing/0)
# NEVER included on first visit...
# OPTIONALLY included on partial reloads...
# ONLY evaluated when needed...
|> assign_prop(:super_expensive_thing, inertia_optional(fn -> calculate_thing() end))
Marks that a prop should be prepended (instead of appended) when merging with existing data on the client-side.
Options
:match_on- A key (or list of keys) used for client-side deduplication of merged items. Each key may be a dot-path into the merged items (e.g."data.id").
@spec inertia_response?(Plug.Conn.t()) :: boolean()
Determines if a response has been rendered with Inertia.
Marks a prop for infinite scroll pagination. Automatically configures merge behavior for the data key and extracts pagination metadata.
Accepts several shapes of paginated data:
- A struct implementing the
Inertia.Paginatedprotocol (e.g.Scrivener.Page) - A
{entries, meta}tuple wheremetaimplementsInertia.Paginated(e.g. Flop's{records, %Flop.Meta{}}) - A map shaped like
%{data: [...], meta: %{...}}(where the entries live under the:wrapperkey)
In every case the entries are placed under the :wrapper key (default "data")
so the resulting prop is uniformly shaped (e.g. %{"data" => [...]}) regardless
of the pagination library. Pagination metadata is emitted separately in
scrollProps, so it is not echoed in the prop value.
Options
:wrapper- The key the data items are placed under (default: "data"):page_name- Override the page query parameter name:scroll_metadata- Custom metadata extraction function forscrollProps(the pagination state the client<InfiniteScroll>component uses; receives the original value). A per-call override of theInertia.Paginatedprotocol.:transform- A 1-arity function applied to each entry before serialization, for shaping entries into a prop-friendly form:meta- A 1-arity function (receiving the original value) whose returned map is placed under a"meta"key in the prop, alongside the entries — for surfacing additional pagination data (totals, etc.) to the page. This is distinct from:scroll_metadata/scrollProps, which drives the scroll component.
Examples
# Basic usage with auto-detected metadata
assign_prop(conn, :users, inertia_scroll(paginated_users))
# Scrivener (via the Inertia.Paginated protocol)
assign_prop(conn, :users, inertia_scroll(MyApp.Repo.paginate(query)))
# Flop ({records, meta} tuple)
assign_prop(conn, :users, inertia_scroll(Flop.run(query, params)))
# With lazy evaluation
assign_prop(conn, :users, inertia_scroll(fn -> User.paginate(params) end))
# Custom wrapper key
assign_prop(conn, :users, inertia_scroll(data, wrapper: "items"))
# Serialize each entry
assign_prop(conn, :users,
inertia_scroll(Flop.run(query, params), transform: &serialize_user/1))
# Custom scroll metadata (overrides the Inertia.Paginated protocol)
assign_prop(conn, :users, inertia_scroll(data, scroll_metadata: fn data ->
%{page_name: "p", current_page: 1, next_page: 2, previous_page: nil}
end))
# Surface extra pagination data under a "meta" key in the prop
assign_prop(conn, :users,
inertia_scroll(Flop.run(query, params),
meta: fn {_records, meta} -> %{total: meta.total_count} end
))
# => %{users: %{data: [...], meta: %{total: 42}}}
@spec preserve_case(raw_prop_key()) :: preserved_prop_key()
Prevents auto-transformation of a prop key to camel-case (when
camelize_props is enabled).
Example
conn
|> assign_prop(preserve_case(:this_will_not_be_camelized), "value")
|> assign_prop(:this_will_be_camelized, "another_value")
|> camelize_props()
|> render_inertia("Home")You can also use this helper inside of nested props:
conn
|> assign_prop(:user, %{
preserve_case(:this_will_not_be_camelized) => "value",
this_will_be_camelized: "another_value"
})
|> camelize_props()
|> render_inertia("Home")
@spec preserve_fragment(Plug.Conn.t()) :: Plug.Conn.t()
Instruct the client-side to preserve the URL fragment across this navigation.
@spec preserve_fragment(Plug.Conn.t(), boolean()) :: Plug.Conn.t()
@spec render_inertia(Plug.Conn.t(), component :: String.t()) :: Plug.Conn.t()
Renders an Inertia response.
Options
ssr: whether to server-side render the response (see the docs on "Server-side rendering" in the README for more information on setting this up). Defaults to the globally-configured value, orfalseif no global config is specified.
Examples
conn
|> assign_prop(:user_id, 1)
|> render_inertia("SettingsPage")You may pass additional props as map for the third argument:
conn
|> assign_prop(:user_id, 1)
|> render_inertia("SettingsPage", %{name: "Bob"})You may also pass options for the last positional argument:
conn
|> assign_prop(:user_id, 1)
|> render_inertia("SettingsPage", ssr: true)
conn
|> assign_prop(:user_id, 1)
|> render_inertia("SettingsPage", %{name: "Bob"}, ssr: true)
@spec render_inertia( Plug.Conn.t(), component :: String.t(), inline_props_or_opts :: map() | render_opts() ) :: Plug.Conn.t()
@spec render_inertia( Plug.Conn.t(), component :: String.t(), props :: map(), opts :: render_opts() ) :: Plug.Conn.t()