Phoenix LiveView with Elixir
Updated: 10 May 2024
Notes based on How to start writing LiveView
Prerequisites
Probably take a look at the Elixir Notes and Phoenix Notes
In order to get started the following are required:
It’s also handy to have the following installed if using VSCode:
Initialize a Project
Firstly, initialize a new Phoenix app called phoenixlive
with:
A few important files we want to be aware of specifically related to the LiveView app are:
lib/elixirlive_web/components/layouts/root.html.heex
which is loaded oncelib/elixirlive_web/components/layouts/app.html.heex
which is updated by LiveView when changes happen
Authentication
Phoenix comes with a generator for building user authentication. In order to do this we can use:
Then, re-fetch dependencies:
And run migrations
Then, start the server with:
You can then visit the application and you will now see a Register and Log In button. You can register to create a new user
Resource
We can generate a live resource called Link:
And then run mix ecto migrate
All of the live views will be in the lib/web/live
directory
This will have generated a lot of content but we’ll delete most of this as we go on
For now though, we can add the live routes to our router.ex
file as directed by the command output:
Make sure that your IDE doesn’t auto import anything when adding routes - I got stuck with an annoying
_live_/0 is undefined
issue because it added an import that had a naming collision with my routes
At this point to avoid potential collisions and issues please also delete the following files:
lib/phoenixlive_web/live/link_live/show.ex
lib/phoenixlive_web/live/link_live/show.html.heex
lib/phoenixlive_web/live/link_live/form_component.ex
Live Views
LiveView implicitly has some callbacks that we should implement
mount
handle_params
render
- implicit when we have a template file
We can clear our resource’s index.ex
file and add the following:
The socket
is like our conn
in a normal route. Every function basically does something to the socket and passes it on and this is effectively how a request is executed from a functional standpoint
And we can update the index.html.heex
to just have some placeholder content for now:
If we start our server again we can visit the /links
page when logged in to see the content of the page we just added
Listing Links for the Current User
We can update our screen to list thelinks for the current user. In order to do this we need to do a few things first:
- Add a reference to the user from our Link schema
- Define a migration for adding the user reference to the database table
- Add a way to list links by user
- Get the user from the current route and use that to list the links
- Display a list of links in the HEEX template
Add Reference to User
To add the reference to the user, we can add the following lines to our schema:
Add a Migration
We can define a migration by generating a new migration with:
Which should generate a migration file into which we add the following:
Then we can apply the migration with:
List Links by User
We can update our list_links
function to take a user_id
. We can implement it using the Ecto Query Syntax:
Get Current User from Route
We can get the current user from the route using the socket.assigns
, this comes from the router :ensure_authenticated
reference that we have in the router.ex
file. Using this data we can list the links for the user and assign
it to the socket which will make it available during render:
Display Links in the HEEX Template
We can access the links using @links
in our template. Phoenix uses this for change tracking in live view which means this data will be re-rendered when changed. We can use the for
attribute to iterate through these values. The resulting template can be seen below:
Creating a Link
We have a way to list links but we don’t have anything in our database at this point, in order to do this we need to add a way to add links. First, we’re going to add a link using the LiveView Link Component. We’re going to use the navigate
property which will give us SPA like naviation along with the ~p
syntax for the route which will type check the URL we use relative to our app router and warn us if it does not exist:
Next, we can define our Live View and make it pass in a form
attribute that can be used by our template to render a form. This makes use of a changeset
and the to_form
method to get the data into the form data structure. Additionally, we need to handle the submit
event so that the user can submit the form and we will create the entry in our database
In the handle_event
when the form is submitted we use Links.create_link
to create a new link in the database using the user_id
from the socket.assigns
. We also use put_flash
which will show a message to the user in the UI as well as push_navigate
which will navigate the user to another URL
When we have an error, we currently assign the changeset back to the form, an example of a changeset which deontes an error can be seen below:
Next, we can add a template for the /links/new
route which references the LiveView Form Component that will use the @form
data we pass into the template. We will also use the .input
component from our lib/phoenixlive_web/components/core_components.ex
file to render the fields for our form:
In the form, we also use the phx-submit
attribute which defines the event that our form submission will fire, this relates directly to the event we defined in our handle_event
method above
And add a reference to this page in the router:
We should be able to visit the /links/new
screen now to view our new page
Making things Live
The thing that makes LiveView really “Live” is the ability to easily work with data and forms and have the result easily visible to a user. To do this, we’ll add a delete button in our list view:
And then we’ll implement the deletion logic similar to how we did for our previous event hander. In this case note that we reassign the :links
property which will update this where it is used in the UI:
Conclusion
The power of live view comes from us being able to write a fairly small amount of code and handle in a fairly straightforward mannner that let’s us quite quickly go from simple forms to automatic live-updating UI without us having to think about it at all