LiveAttribute (LiveAttribute v1.1.4) View Source
LiveAttribute makes binding updateable values easier. To use it add it to your LiveView using use LiveAttribute
and then use the function assign_attribute(socket, subscribe_callback, property_callbacks)
to register attributes.
The attributes will listen to all incoming events and update their assigns of your LiveView automatically, saving
you the hassle of implementing independent handle_info()
and update_...()
calls.
Example using LiveAttribute
defmodule UserLive do
use Phoenix.LiveView
use LiveAttribute
def mount(_params, _session, socket) do
{:ok, assign_attribute(socket, &User.subscribe/0, users: &User.list_users/0)}
end
def handle_event("delete_user", %{"id" => user_id}, socket) do
User.get_user!(user_id)
|> User.delete_user()
{:noreply, socket}
end
end
Same Example without LiveAttribute
defmodule UserLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
if connected?(socket), do: User.subscribe()
{:ok, update_users(socket)}
end
defp update_users(socket) do
users = User.list_users()
assign(socket, users: users)
end
def handle_event("delete_user", %{"id" => user_id}, socket) do
User.get_user!(user_id)
|> User.delete_user()
{:noreply, socket}
end
def handle_info({User, [:user, _], _}, socket) do
{:noreply, update_users(socket)}
end
end
assign_attribute(socket, subscribe, filter \ :_, refresher)
socket
the LiveView socket where the assigns should be executed onsubscribe
the subscribe callback to start the subscription e.g.&Users.subscribe/0
filter
an optional filter if you don't want to update on each event. The filter can either be an expression using:_
as wildcard parameter such as{User, [:user, :_], :_}
. Alternativelyfilter
can be a function with one parameter Note LiveAttribute is issuing each subscribe call in an isolated helper process, so you only need to add filters to reduce the scope of a single subscription.refresher
the function callback to load the new values after a subscription event has fired.
Link to this section Summary
Types
The filter allows doing optimzation by a) ignoring certain events of the subscription source and b) pass event values directly to assign values, instead of using refresher functions to re-load them.
The refresher list is passed to assign_attribute()
to know which assigns to update when
the subscription source issues an event.
Functions
Shortcut version of assign_attribute
to capture an attribute configuration
in a tuple and re-use in multiple LiveViews. This accepts two-element and three-
element tuples with: {subscribe, refresher}
or {subscribe, filter, refresher}
correspondingly
socket = assign_attribute(socket, &User.subscribe/0, users: &User.list/0)
assign_attribute
updates the specified assign keys each time there is a new event sent from
the subscription source.
Returns a specification to start this module under a supervisor.
socket = update_attribute(socket, :users)
Helper method to issue a update callback manually on a live attribute, when there is a known update but no subscription event.
Link to this section Types
Specs
The filter allows doing optimzation by a) ignoring certain events of the subscription source and b) pass event values directly to assign values, instead of using refresher functions to re-load them.
It can be either a match object defining which events should be matched, or a function
returning false
when the event should be ignored or a map when it should be processed.
Keys that are present in the map will be assigned to the socket. (if there are matching
keys in the refresher list)
Filter function
The filter function receives the event and should return either false
or a map of
the new values:
fn event ->
case event do
{User, :users_updated, users} -> %{users: users}
_ -> false
end
end)
Filter object
Match objects are defined by example of a matching list or tuple. These can be customized using two special terms:
:_
the wildcard which matches any value, but ignores it{:"$", some_key}
- which matches any value, and uses it as update value in the socket assigns
Examples
# Let's assumg the `User` module is generating the following event each time
# the user list is updated: `{User, :users_updated, all_users}`
# then the following match object will extract the users
{User, :users_updated, {:"$", :users}}
# Full function call with match object
assign_attribute(socket, &User.subscribe/0, users: &User.list/0, {User, :users_updated, {:"$", :users}})
# Now the same we could get with this function callback instead:
fn event ->
case event do
{User, :users_updated, users} -> %{users: users}
_ -> false
end
end)
# Full function call with callback
assign_attribute(socket, &User.subscribe/0, users: &User.list/0,
fn event ->
case event do
{User, :users_updated, users} -> %{users: users}
_ -> false
end
end)
Specs
The refresher list is passed to assign_attribute()
to know which assigns to update when
the subscription source issues an event.
It is a list of {key, callback}
pairs specifying how to load the new attribute values.
The callback
thereby can have optionally one argument to read context from the socket.
refresher() examples
# 1. Zero argument callback to update the users list:
[users: &User.list_all/0]
# 2. Single argument callback to use the socket state in the update:
[users: fn socket ->
User.list_all() -- socket.assigns.blacklist
end]
# 3. Special `socket` key to assign multiple values at once manually
[socket: fn socket ->
assign(socket,
users: User.list_all() -- socket.assigns.blacklist,
last_update: System.os_time()
)
end]
Usage Examples
iex> assign_attribute(socket, &User.subscribe(), users: &User.list_all/0)
iex> assign_attribute(socket, &User.subscribe(), fn socket ->
assign(socket, users: User.list_all() -- socket.assigns.blacklist)
end)
Specs
Link to this section Functions
Specs
Shortcut version of assign_attribute
to capture an attribute configuration
in a tuple and re-use in multiple LiveViews. This accepts two-element and three-
element tuples with: {subscribe, refresher}
or {subscribe, filter, refresher}
correspondingly
Use with:
socket = assign_attribute(socket, User.live_attribute())
When there is an User
method:
defmodule User do
def live_attribute() do
{&subscribe/0, users: &list_users/0}
end
...
end
Specs
socket = assign_attribute(socket, &User.subscribe/0, users: &User.list/0)
assign_attribute
updates the specified assign keys each time there is a new event sent from
the subscription source.
See refresher()
and filter()
for advanced usage of these parameters. Simple usage:
Returns a specification to start this module under a supervisor.
See Supervisor
.
socket = update_attribute(socket, :users)
Helper method to issue a update callback manually on a live attribute, when there is a known update but no subscription event.