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

iPaul
page 37 ANTLRInputStream input = new ANTLRInputStream(is); as of ANTLR 4 .8 should be: CharStream stream = CharStreams.fromStream(i...
New
brianokken
Many tasks_proj/tests directories exist in chapters 2, 3, 5 that have tests that use the custom markers smoke and get, which are not decl...
New
fynn
This is as much a suggestion as a question, as a note for others. Locally the SGP30 wasn’t available, so I ordered a SGP40. On page 53, ...
New
oaklandgit
Hi, I completed chapter 6 but am getting the following error when running: thread 'main' panicked at 'Failed to load texture: IoError(O...
New
dsmith42
Hey there, I’m enjoying this book and have learned a few things alredayd. However, in Chapter 4 I believe we are meant to see the “&gt;...
New
taguniversalmachine
Hi, I am getting an error I cannot figure out on my test. I have what I think is the exact code from the book, other than I changed “us...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New
rainforest
Hi, I’ve got a question about the implementation of PubSub when using a Phoenix.Socket.Transport behaviour rather than channels. Before ...
New
New
dachristenson
@mfazio23 Android Studio will not accept anything I do when trying to use the Transformations class, as described on pp. 140-141. Googl...
New

Other popular topics Top

AstonJ
I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl: The Ser...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
PragmaticBookshelf
Rails 7 completely redefines what it means to produce fantastic user experiences and provides a way to achieve all the benefits of single...
New
AstonJ
If you want a quick and easy way to block any website on your Mac using Little Snitch simply… File &gt; New Rule: And select Deny, O...
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
New
PragmaticBookshelf
Develop, deploy, and debug BEAM applications using BEAMOps: a new paradigm that focuses on scalability, fault tolerance, and owning each ...
New
AstonJ
This is cool! DEEPSEEK-V3 ON M4 MAC: BLAZING FAST INFERENCE ON APPLE SILICON We just witnessed something incredible: the largest open-s...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
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: