Easy UI Toggling with LiveView JS Commands · The Phoenix Files

The Toggling UI Element with LiveView

Here’s Sophie DeBenedetto, co-author of Programming Phoenix LiveView with a deep dive into building a toggling UI element with LiveView’s JS.toggle command.

LiveView empowers developers to be more productive by keeping your mind focused on the server while building rich interactive UIs. Recently, LiveView has improved its ability to manage client-side interactions through JS commands. This post will guide you through building a toggle feature using JS.toggle.

The Problem

You want to toggle the appearance of content in response to user interactions. For example, showing/hiding a search form when clicking the “search” icon. Without JS commands, you’d need to send events to the server to manage the UI state.

The Solution

The Phoenix.LiveView.JS module provides functionality to invoke client-side operations in response to LiveView events. It abstracts common JS interactions like showing/hiding content, adding/removing classes, and setting CSS attributes.

Example: Toggling a Form

We'll look at an example to create a toggling feature for a hidden form:

# lib/live_library_web/live/book_live/search_component.html.heex

<.form
  let={f}
  for={@search_changeset}
  id="book-search-form"
  phx-submit="search"
  phx-target={@myself}
  class={@search_class} >

To make the form hidden on page load, we’ll use CSS:

/* assets/css/app.css */

#book-search-form {
 display: none;
}

Now, let’s make it toggle using Phoenix.LiveView.JS.toggle/1:

def icon(assigns) do
  ~H"""
  <i phx-click={JS.toggle(to: "#book-search-form")} class="bi bi-search"></i>
  """
end

Now, clicking the search icon will toggle the form's visibility.

LiveView JS Commands Under the Hood

The JS.toggle/1 function generates a Phoenix.LiveView.JS struct for toggling:

%Phoenix.LiveView.JS{
  ops: [
    [
    "toggle",
      %{
        display: nil,
        ins: [[], [], []],
        outs: [[], [], []],
        time: 200,
        to: "#book-search-form"
      }
    ]
  ]
}

LiveView binds the phx-click event to the appropriate JS actions when rendering the template.

Trigger Client- and Server-Side Interactions Together

You can chain JS commands to push messages to the server while toggling the search form on the client:

def icon(assigns) do
  ~H"""
  <i phx-click={JS.push("toggle_book_search", target: @target) |> JS.toggle(to: "#book-search-form")} class="bi bi-search"></i>
  """
end

This allows the search form to toggle and URL updates without forcing a page reload. The event handler for "toggle_book_search" can manage the URL changes conveniently:

def handle_event("toggle_book_search", _value, socket) do
  if socket.assigns.search_class == "show" do
    {:noreply, push_patch(socket, to: "/books")}
  else
    {:noreply, push_patch(socket, to: "/books?search")}
  end
end

This approach fosters a seamless user experience where interactions feel fluid without unnecessary reloads.

JS Commands Give Us Simple and Powerful Client-Side Interactions

LiveView abstracts common JS functionalities, allowing you to implement features without writing JavaScript. Explore more in the docs.