djantea

djantea

Building Table Views with Phoenix LiveView: Add a load indicator that will start on user event (click) and end when data finishes loading

Hello @pullrich,

I am following he book, which is great because is helping me building tabelar UI fast. Thank you.

The ideea to separate the individul Phoenix.LiveComponents (sorting, filtering, pagination) from the main LiveView by sending events is great, but it does introduce a UX problem: there is no load indictor that will cover the whole time, starting with the user event (e.g. click on the page number of the pagination form) and ending when the result (the paginated meerkat data) is sent back to the browser.

The current behavior is that the load indicator starts when the user clinks the button and stops when the push event is handled by the handle_event callback. But in the background, the processing of actually fetching the data continues by sending an :update event to the parent LiveView, changing the URI params with push_patch and finally responding to the the URI params change with handle_params. All this subsequent background processing is not covered by the load indicator, and this is normal, as this is a custom way of processing the data. So we need to manually start and stop the progress indicator ourselves.

I tried to solve this problem myself but did not succeed:

I can start the load indicator (the topbar displayed at the top of the page) by replacing:

<div phx-click="show_page"​
     phx-value-page={page_number}​
     phx-target={@myself}​
     class={if current_page?, do: "active"} >

with

<div phx-click={JS.push("show_page") |> JS.dispatch("phx:page-loading-start")}
     phx-value-page={page_number}​
     phx-target={@myself}​
     class={if current_page?, do: "active"} >

But I cannot stop the indicator. I tried by adding this to MeowWeb.MeerkatLive.handle_params/3:

   ...
   |> push_event("phx:page-loading-stop", %{})
   ...

but it does not work. I see the event being sent to the browser through the web socket, but there is no effect, the progress indicator keeps going on.

Any ideas?

Marked As Solved

pullrich

pullrich

Author of Building Table Views with Phoenix LiveView

Hey @djantea ,

thank you for reading my book :slight_smile:

I must admit that it took me a good hour to find the problem here: If you push an event from the server to the client using push_event/3, then you don’t have to prefix the event name with phx: because LiveView does so automatically.

So, in your case, the client would receive the event phx:phx:page-loading-stop which doesn’t stop the loading bar. If you remove the phx: from your push_event/3-call, it should work :slight_smile:

Let me know if this fixed your problem :muscle:

Also Liked

djantea

djantea

Hy @pullrich,

Thank you for taking the time and for responding.

Yes, it solved my problem :slight_smile:

I also realized what the problem was, the next day after I posted the question.

Taking the solution one step further, I decided to use different, custom JavaScript events to be triggered for table loading, phx:table-loading-start and phx:table-loading-stop:

  • After the initial user event is handled by any of the three LiveComponents (clicking on the sort header, on the filter button, or on the page number), right after sending the :update event to the parent LiveView in handle_event/3, I trigger a phx:table-loading-start event:
    ...
    send(self(), {:update, opts})
    {:noreply, push_event(socket, "table-loading-start", %{})}
    ...
  • Then, in the parent LiveView, after the final data is available and is ready to be sent to the browser, in handle_params/3, I trigger a phx:table-loading-stop event:
  def​ handle_params(params, _url, socket) ​do​
    socket =
      socket
      |> parse_params(params)
      |> assign_meerkats()
      |> push_event("table-loading-stop", %{})

      {​:noreply​, socket}
  ​end

And then, I handle these event in JavaScript by starting and stopping the page loading progress indicator, while also preventing the handler of the LiveView’s phx:page-loading-stop event to stop the progress if it has been started by a phx:table-loading-start event:

window.tableLoading = false;

window.addEventListener('phx:table-loading-start', info => {
  window.tableLoading = true
  topbar.show()
})
window.addEventListener('phx:table-loading-stop', info => {
  window.tableLoading = false
  topbar.hide()
})

window.addEventListener("phx:page-loading-start", info => {
  topbar.show()
})
window.addEventListener("phx:page-loading-stop", info => {
  if (!window.tableLoading)
    topbar.hide()
})

I think this is a cleaner solution, because it separates the table loading events, which are spanning across more than one LiveView event → handle_event cycle, from LiveView events which are more fine grained.

But I admit its not the cleanest approach, and I am still looking to improve it.

I feel that more control over the page loading (and maybe other kinds of loading) events should be exposed by LiveView itself, for situations like this.

What do you think?

Where Next?

Popular Pragmatic Bookshelf topics Top

jamis
The following is cross-posted from the original Ray Tracer Challenge forum, from a post by garfieldnate. I’m cross-posting it so that the...
New
lirux
Hi Jamis, I think there’s an issue with a test on chapter 6. I own the ebook, version P1.0 Feb. 2019. This test doesn’t pass for me: ...
New
herminiotorres
Hi @Margaret , On page VII the book tells us the example and snippets will be all using Elixir version 1.11 But on page 3 almost the en...
New
jskubick
I’m running Android Studio “Arctic Fox” 2020.3.1 Patch 2, and I’m embarrassed to admit that I only made it to page 8 before running into ...
New
digitalbias
Title: Build a Weather Station with Elixir and Nerves: Problem connecting to Postgres with Grafana on (page 64) If you follow the defau...
New
adamwoolhether
Is there any place where we can discuss the solutions to some of the exercises? I can figure most of them out, but am having trouble with...
New
hazardco
On page 78 the following code appears: &lt;%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
Keton
When running the program in chapter 8, “Implementing Combat”, the printout Health before attack was never printed so I assumed something ...
New
dtonhofer
@parrt In the context of Chapter 4.3, the grammar Java.g4, meant to parse Java 6 compilation units, no longer passes ANTLR (currently 4....
New
gorkaio
root_layout: {PentoWeb.LayoutView, :root}, This results in the following following error: no “root” html template defined for PentoWeb...
New

Other popular topics Top

PragmaticBookshelf
Machine learning can be intimidating, with its reliance on math and algorithms that most programmers don't encounter in their regular wor...
New
PragmaticBookshelf
Write Elixir tests that you can be proud of. Dive into Elixir’s test philosophy and gain mastery over the terminology and concepts that u...
New
Exadra37
Please tell us what is your preferred monitor setup for programming(not gaming) and why you have chosen it. Does your monitor have eye p...
New
AstonJ
Do the test and post your score :nerd_face: :keyboard: If possible, please add info such as the keyboard you’re using, the layout (Qw...
New
PragmaticBookshelf
Create efficient, elegant software tests in pytest, Python's most powerful testing framework. Brian Okken @brianokken Edited by Kat...
New
AstonJ
Saw this on TikTok of all places! :lol: Anyone heard of them before? Lite:
New
AstonJ
We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New
AstonJ
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
New
mindriot
Ok, well here are some thoughts and opinions on some of the ergonomic keyboards I have, I guess like mini review of each that I use enoug...
New

Sub Categories: