LiveProps is a library for managing properties and state within Phoenix LiveViews and LiveComponents. This is my first package with Elixir so any feedback is welcome.
Features
- Declaratively define props and state, initialize default values, and compute derived values.
- Supports required props
- Supports automatic re-computation of computed props and state
- Props automatically added to module documentation.
Here is a simple example:
defmodule ButtonComponent do
use Phoenix.LiveComponent
use LiveProps.LiveComponent
prop :class, :string, default: "button"
prop :text, :string, default: "Click me"
prop :on_click, :string, default: "click_button"
def render(assigns) do
~L"""
<button class="<%= @class %>"
phx-click="<%= @on_click %>">
<%= @text %>
</button>
"""
end
end
In this example we define three props that will be automatically assigned default values, so you don’t have to define your own mount or update callbacks (but you still can if you want and the defaults will still be assigned, unless you overwrite them)
Additional examples in the docs.
Docs
Hex
Github
Thanks
8 Likes
Wow this is really cool, and not very intrusive.
How would it look like if I want to define my own mount/1
and update/2
functions?
Thanks. Defining your own callbacks is no problem. Two things to know are that, for a component:
- states are assigned in mount/1 and props are assigned in update/2 (or preload/1 if it is defined).
- any callbacks you defined will be executed after the injected callbacks (so defaults/computed values will already be assigned).
Lastly, if you don’t want any of this behavior in a particular component or LiveView, that is fine. You don’t have to use it everywhere.
In code:
defmodule ThermostatComponent do
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 mount(socket) do
# socket.assigns has :mode == :verbose
# but props are not assigned yet
# do something
{:ok, socket}
end
def update(assigns, socket) do
# Assigns are the original assigns passed in to the component.
# They have already been merged into the socket.assigns so you
# do not have to do it again.
# Additionally, the :temperature prop has already been computed and assigned.
# do something
{:ok, socket}
end
def get_temperature(assigns) do
Thermostat.get_user_reading(assigns.user_id)
end
def handle_event("toggle-mode", _, %{assigns: assigns} = socket) do
new_mode = if assigns.mode == :verbose, do: :compact, else: :verbose
{:noreply, assign(socket, :mode, new_mode)}
end
end
2 Likes