Subscribe to access all episodes. View plans →
Published January 16, 2023
0.3.2
but in version 0.3.3
the default_value
option has been changed to value
Phoenix 1.6
Follow along with the episode starter on GitHub
Here we have our demo album application. And if we select an album - we see some details about it. Now when we go to edit the album the genre field is a dropdown with a lot of different choices. And because this dropdown is pretty long it can take a while to find and select the correct genre.
It would be nice to have a more dynamic user experience where instead of looking through this list we could just start typing the genre we want and it would filter the choices for us - making for a better user experience. Since we’re using LiveView there’s a great library we can use to do just this called LiveSelect
.
LiveSelect
is a LiveView component that implements a dynamic selection field with a dropdown. Let’s use this for our genre field. To get started we’ll go to Hex and grab the live_select
config then we’ll open our Mixfile and add it to our list of dependencies.
mix.exs
...
defp deps do
[
...
{:live_select, "0.3.2"},
...
]
end
...
Then let’s go to the command line and run mix deps.get
.
$ mix deps.get
...
New:
live_select 0.3.2
* Getting live_select (Hex package)
Our example app is using Tailwind CSS and since LiveSelect supports Tailwind, let’s go ahead and configure it to use the standard tailwind utility classes. We’ll open tailwind.config.js
and add the following line to the content
section of the config.
assets/tailwind.config.js
...
module.exports = {
content: [
...
'../deps/live_select/lib/live_select/component.*ex'
...
]
]
...
LiveSelect
also needs JS to work so let’s open our app.js
and we’ll want to import live_select
and then add the LiveSelect hook to our live socket.
assets/js/app.js
...
import live_select from "live_select"
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: live_select})
...
Now our album form is handled by the form_component.ex
so let’s open that and we’ll need to import LiveSelect
in order to use the live_select
form helper.
lib/teacher_web/live/album_live/form_component.ex
defmodule TeacherWeb.AlbumLive.FormComponent do
...
import LiveSelect
...
end
Great now let’s open the corresponding form_compononent.html.heex
template and here we can replace the select
form helper that we are currently using for our genre with the live_select
. If we look at the docs we can see there are several different options that you can use to customize your live_select
field.
Let’s update our genre field to use the live_select
. We’ll go ahead and set the default_value
to be the album’s genre and let’s set the placeholder
to be the album’s genre as well. Also, let’s include the update_min_len
option with a value of 1. This is the minimum length of text in the text input field that will trigger an update of the dropdown.
Template path: lib/teacher_web/live/album_live/form_component.html.heex
...
<%= label f, :genre %>
<%= live_select f, :genre,
default_value: @album.genre,
placeholder: @album.genre,
update_min_len: 1 %>
<%= error_tag f, :genre %>
...
Now when a user starts to type a genre to select in our new live_select
field, it will send a LiveSelect.ChangeMsg
message to our LiveView - we need to update that corresponding LiveView
to handle this message.
Let’s open the LiveView that will handles this - our show.ex
LiveView and we’ll add a handle_info
callback….pattern matching on the %LiveSelect.ChangeMsg{}
. Then once we have our message, we can grab the text
property, which contains the current text entered by the user. We’ll need to take this partial text and use it to filter the list of genres. To do this our application has a function Genre.search
. It’s not important here to know how it works, just know that it takes some text and then returns a list of genres that best match that text.
This list of matching genres will be displayed by the LiveSelect dropdown. With this list we can update our LiveSelect by calling LiveSelect.update_options(change_msg, genres)
. Then we’ll need to return a :noreply
tuple. And let’s add an alias for the genre module so we can call it without the prefix.
lib/teacher_web/live/album_live/show.ex
defmodule TeacherWeb.AlbumLive.Show do
...
alias Teacher.Recordings.Genre
...
@impl true
def handle_info(%LiveSelect.ChangeMsg{} = change_msg, socket) do
genres = Genre.search(change_msg.text)
LiveSelect.update_options(change_msg, genres)
{:noreply, socket}
end
...
end
With that let’s test out our changes. We’ll go to the command line and start our server.
$ mix phx.server
...
Now when we edit our album and start typing a genre, we can see a dropdown with an updated list of genres appears as we type. And if we select the genre and save the album - it works. This is great, but some of our albums might be in multiple genres - how can we handle that? Fortunately LiveSelect has a tag mode which makes it easy for us to do just that. Let’s go back to the form_component.html.heex
and set it to use tags mode. With this enabled our selected genres will be sent to our LiveView as a list. So to make it easier for us to handle this format, let’s update our field to use the album’s genre_list
.
If we take a quick look at our Album module, we can see that the genre_list
field is an array of strings, which is what we want. Then let’s go back to our template and update the default_value
to use the genre_list
and then for the placeholder, let’s join any genres present and separate them with a comma. Our join will fail if genre_list
is nil
, so let’s go ahead and include an empty list to use if it is.
Template path: lib/teacher_web/live/album_live/form_component.html.heex
...
<%= label f, :genre %>
<%= live_select f, :genre_list,
mode: :tags,
default_value: @album.genre_list,
placeholder: Enum.join(@album.genre_list || [], ", "),
update_min_len: 1 %>
<%= error_tag f, :genre_list %>
...
Because we’re now using the genre_list
field, let’s open our album’s show template and update it to display our genre_list
using the same formatting we did for the placeholder.
Template path: lib/teacher_web/live/album_live/form_component.html.heex
<li>
<strong>Genre:</strong>
<%= Enum.join(@album.genre_list || [], ", ") %>
</li>
With that let’s go back to the browser and if we now try to edit our album’s genre we’re able to select one genre and then another. This is great and the selected genres are automatically displayed above our field!
We can also remove one and select a replacement. And great they are saved and album is updated with multiple genres. LiveSelect
made it easy to update our LiveView to use a dynamic selection field with a dropdown.
Romenig Lima Damasio
1 year agoit’s nice. But I would like to see the options for select in a dropdown as before. It’s just searching.