live_props v0.1.0 LiveProps View Source
LiveProps is a library for managing properties and state within Phoenix LiveViews and Phoenix LiveComponents.
Features
Declaratively define props and state, initialize default values, and compute derived values using the
LiveProps.prop/3
andLiveProps.state/3
macros.Supports required props
Supports asynchronous loading of state.
Supports automatic re-computation of computed props and state
Props automatically added to module documentation.
Example
Inside a LiveView or LiveComponent, you must use LiveProps.LiveView
or LiveProps.LiveComponent
,
respectively. LiveComponents can have state and props, while a LiveView can only have state,
so we'll look at an example LiveComponent to demonstrate both.
defmodule MyAppWeb.ThermostatComponent do
# If you generated an app with mix phx.new --live,
# the line below would be: use MyAppWeb, :live_component
use Phoenix.LiveComponent
use LiveProps.LiveComponent
prop :user_id, :integer, required: true
prop :temperature, :float, compute: :get_temperature
state :mode, :atom, default: :verbose
def render(assigns) do
~L"""
<%= case @mode do %>
<% :verbose -> %>
Current temperature: <%= @temperature %>
<% _ -> %>
<%= @temperature %>
<% end %>
<button phx-click="toggle-mode" phx-target="<%= @myself %>">Toggle mode</button>
"""
end
def get_temperature(%{assigns: assigns} = _socket) do
Thermostat.get_user_reading(assigns.user_id)
end
def handle_event("toggle-mode", _, socket) do
new_mode =
if socket.assigns.mode == :verbose,
do: :compact,
else: :verbose
{:noreply, assign(socket, :mode, new_mode)}
end
end
Our component requires a :user_id
prop, which it uses to fetch the temperature.
Since it is required, an error will be raised if you forget to pass it in.
We also have the :temperature
prop, which is a computed prop. This will be re-calculated
automatically anytime the :user_id prop changes. It is calculated by get_temperature/1
which
takes the socket as an argument and returns the value to be assigned. Calculations are run
in the order defined so we could add even more computed props which depend on the temperature assign.
Lastly, the component has a state called :mode
which controls the display. We've given
it a default value, which is assigned on mount. We could also add computed states
which depends on other states.
Notice what our component does not have: a Phoenix.LiveComponent.mount/1
or Phoenix.LiveComponent.update/2
callback. LiveProps handles that for you, by injecting lightweight mount/1 and update/2 callbacks under the hood.
In pseudocode, these callbacks look like the following:
def mount(socket) do
{:ok, assign_default_states(socket)}
end
def update(assigns, socket) do
raise_if_missing_required_props!(assigns)
{:ok, assign_props_and_computed_props(socket)}
end
While LiveProps defines mount
and update
callbacks for you. You can still define your own
and everything will continue to work. In a LiveComponent, any mount or update callbacks
you define will be run after the the LiveProps callbacks (i.e. defaults and computed values
will already be assigned to the socket)
This module is not intended to be used directly but rather by means of
LiveProps.LiveView
and LiveProps.LiveComponent
. Please see docs for those
modules for additional information.
Link to this section Summary
Functions
Define a property with the given name and type. Returns :ok
Define state of given name and type. Returns :ok.
Link to this section Functions
Specs
Define a property with the given name and type. Returns :ok
This macro is meant to be called within a LiveComponent only. Types can be any atom and are just for documentation purposes.
Options:
:default
- A default value to assign to the prop.:required
- boolean. If true, an error will be raised if the prop is not passed to the component.:compute
- 1-arity function that takes the socket as an argument and returns the value to be assigned. Can be an atom of the name of a function in your component or a remote function call like&MyModule.compute/1
Specs
Define state of given name and type. Returns :ok.
Types can be any atom and are just for documentation purposes.