Fundamentals
LiveView Assigns
Read time: 5 minutes
#Storing state
A very important feature of a frontend framework is being able to store the state of the current application. ReactJS uses hooks, VueJS uses composition/options API and so on. In LiveView we call the state of a view assigns
(plural form).
assigns
are just an Elixir map. You can store in the assigns
map everything that you could store in any variable: lists, maps, structs, etc.
A great place to write assigns
when your LiveView is generated is in a callback
called mount/3
.
Let's create a file called assigns.exs
:
Mix.install([
{:liveview_playground, "~> 0.1.1"}
])
defmodule PageLive do
use LiveviewPlaygroundWeb, :live_view
def mount(_params, _session, socket) do
socket = assign(socket, name: "Lubien")
{:ok, socket}
end
def render(assigns) do
~H"""
Hello <%= @name %>
"""
end
end
LiveviewPlayground.start()
Start the server as elixir assigns.exs
and see the result.
If you're feeling desperate right now, don't be. I know 7 new things were added in just 5 new lines of code compared to hello_liveview.exs
but let's break down these modifications one at a time!
#The mount/3
callback
The way in which the LiveView framework sends information so that programmers can process the data is through callbacks. These are nothing more than functions that run when something happens. The mount/3
callback runs when your LiveView is initialized. Its three arguments are, respectively:
-
Parameters coming from the URL. Useful for routes like
/users/:id
where:id
would be one of the parameters. - Data from the current browsing session (if configured). Useful for authentication.
-
Data from the current connection with the user accessing this LiveView in a data structure called Phoenix.LiveView.Socket, better known simply as
socket
.
The first two arguments will be explored in more detail in future guides. Right now we will just ignore them for the sake of simplicity.
#The %Socket{} data structure
Let's get straight to the point: LiveView state management completely revolves around modifying the state of your socket. The function assigns/2
receives your socket
and the assigns you want to add and applies them generating a new socket. Let's try! Update your code as follows:
def mount(_params, _session, socket) do
socket.assigns |> dbg
socket = assign(socket, name: "Lubien")
socket.assigns |> dbg
{:ok, socket}
end
Don't forget to turn the server off and on again to see the changes.
dbg/2
dbg/2
macro is extremely useful for debugging code and we will be using it a lot during classes. In general, it will be used by adding a pipe to it like |> dbg
. It tells you the file name, line, function and variables of the thing you are debugging. Super powerful.
If you check your terminal you will see information like this:
[priv/examples/mount-and-assigns/assigns.exs:9: PageLive.mount/3]
socket.assigns #=> %{__changed__: %{}, flash: %{}, live_action: :index}
[priv/examples/mount-and-assigns/assigns.exs:11: PageLive.mount/3]
socket.assigns #=> %{name: "Lubien", __changed__: %{name: true}, flash: %{}, live_action: :index}
As we can see, assigns are just a map with some data about your LiveView. Explaining each one:
-
__changed__
: is a map that LiveView automatically populates when something changes in order to explain to its HTML rendering engine which properties need to be updated to generate the final HTML in an efficient way. -
flash
: is a map used to send information, success and alert messages to its users. We will use it in the future. -
live_action
: when we get into the subject of Router we will see that we can use this data to identify where we are in the application.
Furthermore, we can notice that in the second dbg
we already have new data, the name
was added.
#Rendering assigns
Let's look at our render function once more:
def render(assigns) do
~H"""
Hello <%= @name %>
"""
end
The way we render assigns in a LiveView is by using <%= %>
. The documentation calls these tags while I personally prefer to call it interpolation. Furthermore, to access the assign called name
just use the shortcut @name
.
Behind the scenes, inside a render function using @name
is exactly the same as assigns.name
. Remember that I said that the only argument of a render function was necessarily called assigns
? See what happens if I rename it to any other name:
$ elixir priv/examples/mount-and-assigns/assigns.exs
** (RuntimeError) ~H requires a variable named "assigns" to exist and be set to a map
(phoenix_live_view 0.18.18) expanding macro: Phoenix.Component.sigil_H/2
priv/examples/mount-and-assigns/assigns.exs:16: PageLive.render/1
However, if I change my render function to:
def render(assigns) do
~H"""
Hello <%= assigns.name %>
"""
end
Everything works normally.
#Recap!
-
The
mount/3
callback runs when your LiveView is initializing. -
The
socket
data structure contains the state of your LiveView for this user at the moment. -
We were able to add
assigns
using theassigns/2
function passing thesocket
and the new values. -
The
render/1
function has a shortcut for writing assigns using@assign_name
.
Feedback
Got any feedback about this page? Let us know!