Tutorial
Improving LiveView UX with Phoenix Channels - Tagging part 3
This post was updated 01 May - 2020
In the previous tutorial, I set up the tagging interface. It had, however, a small issue. If I added a tag, it didn't really refocus on the input, so I needed to manually set the mouse there again.
In this tutorial, I will show how I can broadcast a message using Phoenix Channels to be handled by channels javascript and then simply set focus using javascript.
This tutorial also builds upon this tutorial where I set up an AppChannel
that uses a channel name that is concatenated like:
"app:#{csrf_token}"
And to pass in the csrf_token
into the session so I can use it in all LiveView components is covered in this tutorial.
STEP 1 - LiveView component changes
I basically need to do 3 changes here.
- In the mount function, extract
csrftoken
from session and add a channelname to assigns - Add a
refocus_input/1
function where I actually do the broadcast from - Call
refocus_input/1
when a tag is added
Start in the mount function. Note that the csrf_token
comes from a plug that I set up in a previous tutorial.
def mount(%{"id" => product_id, "csrf_token" => csrf_token} = _session, socket) do
product = get_product(product_id, connected?(socket))
assigns = [
...
# CHANNEL NAME
channel_name: "app:#{csrf_token}",
...
]
{:ok, assign(socket, assigns)}
end
Next step is to add the function where I do the actual broadcast from. Note that I specify the id of the input field. And the event name is "focus"
. So in theory, I could use the same JS for different focus events that should focus on different dom elements.
defp refocus_input(socket) do
TutorialWeb.Endpoint.broadcast_from(
self(),
socket.assigns.channel_name,
"focus",
%{id: "tagging-form"}
)
socket
end
Last part there is to add refocus_input(socket)
def handle_event("pick", %{"name" => search_phrase}, socket) do
product = socket.assigns.product
taggings = add_tagging_to_product(product, search_phrase)
refocus_input(socket)
...
end
STEP 2 - Frontend changes
I basically just need to do 2 things.
- Add an id to the input field
- Add a channel callback in JS
So open the template and add id="tagging-form"
inside the input.
# lib/tutorial_web/templates/product/product_tagging.html.leex
<%= form_tag "#", [phx_change: :search, phx_submit: :submit] do %>
...
<input
type="text"
id="tagging-form"
class="inline-block text-sm focus:outline-none"
name="search_phrase"
value="<%= @search_phrase %>"
phx-debounce="500"
placeholder="Add tag"
>
...
<% end %>
And open the socket.js
and add
// assets/js/socket.js
channel.on("focus", msg => {
const elm = document.getElementById(msg['id'])
elm.focus()
elm.value = ''
})