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

jimschubert
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
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
hazardco
On page 78 the following code appears: &lt;%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
akraut
The markup used to display the uploaded image results in a Phoenix.LiveView.HTMLTokenizer.ParseError error. lib/pento_web/live/product_l...
New
mert
AWDWR 7, page 152, page 153: Hello everyone, I’m a little bit lost on the hotwire part. I didn’t fully understand it. On page 152 @rub...
New
tkhobbes
After some hassle, I was able to finally run bin/setup, now I have started the rails server but I get this error message right when I vis...
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
roadbike
From page 13: On Python 3.7, you can install the libraries with pip by running these commands inside a Python venv using Visual Studio ...
New
dachristenson
I’ve got to the end of Ch. 11, and the app runs, with all tabs displaying what they should – at first. After switching around between St...
New

Other popular topics Top

AstonJ
poll poll Be sure to check out @Dusty’s article posted here: An Introduction to Alternative Keyboard Layouts It’s one of the best write-...
New
AstonJ
There’s a whole world of custom keycaps out there that I didn’t know existed! Check out all of our Keycaps threads here: https://forum....
New
Margaret
Hello everyone! This thread is to tell you about what authors from The Pragmatic Bookshelf are writing on Medium.
1147 28379 760
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
PragmaticBookshelf
Author Spotlight Rebecca Skinner @RebeccaSkinner Welcome to our latest author spotlight, where we sit down with Rebecca Skinner, auth...
New
New
PragmaticBookshelf
Author Spotlight: VM Brasseur @vmbrasseur We have a treat for you today! We turn the spotlight onto Open Source as we sit down with V...
New
First poster: bot
zig/http.zig at 7cf2cbb33ef34c1d211135f56d30fe23b6cacd42 · ziglang/zig. General-purpose programming language and toolchain for maintaini...
New
PragmaticBookshelf
Develop, deploy, and debug BEAM applications using BEAMOps: a new paradigm that focuses on scalability, fault tolerance, and owning each ...
New
sir.laksmana_wenk
I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc. However, I don’t...
New

Sub Categories: