kuon

kuon OP

I am currently trying a mixed approach where I would still use live view for rendering.

Basically, instead of rendering the whole table, I render only the visible part (in a live view), and I push the coordinates when scrolling.

It is still early stage but I think this road should work while requiring very minimal JS.

0
Post #11
kuon

kuon OP

Ok, I ended up with the following solution:

  • set a hook on the container like so:

Hooks.table = {
  mounted () {
    this.resize()
    this.resizeListener = () => this.resize()
    this.scrollListener = (evt) => this.scroll(evt)
    window.addEventListener('resize', this.resizeListener)
    this.el.addEventListener('wheel', this.scrollListener)
  },
  destroyed () {
    window.removeEventListener('resize', this.resizeListener)
    this.el.removeEventListener('wheel', this.scrollListener)
    this.resizeListener = null
  },
  resize () {
    this.rect = this.el.getBoundingClientRect()
    this.pushEventTo('#table-component', 'resize', this.rect)
  },
  scroll (evt) {
    let d = evt.deltaY
    if (d === 0) {
      return
    }
    switch (evt.deltaMode) {
      case WheelEvent.DOM_DELTA_PIXEL:
        break;
      case WheelEvent.DOM_DELTA_LINE:
        d *= 16;
        break;
      case WheelEvent.DOM_DELTA_PAGE:
        d *= this.rect.height / 2;
        break;
    }
    let off = {x: 0, y: 0}
    if (evt.shiftKey) {
      off.x = d
    } else {
      off.y = d
    }
    this.pushEventTo('#table-component', 'scroll', off)
  }
}

have a simple handler like this:


  @impl true
  def handle_event("resize", %{"width" => width, "height" => height}, socket) do
    {:noreply,
     assign(socket, viewport: %{width: width, height: height})
     |> do_fetch_data()}
  end

  @impl true
  def handle_event("scroll", %{"x" => x, "y" => y}, socket) do
    offset = socket.assigns.offset

    offset = %{
      x: offset.x + x,
      y: offset.y + y
    }

    {:noreply, assign(socket, offset: offset) |> do_fetch_data()}
  end

Then in the do_fetch_data I clamp the offset, and I gather the data rows for the given view.

I then just render that “window” on my large data table.

This approach doesn’t provide native scrolling, but it is very fast and I can stream over my large table. The good side is very minimal javascript code (of course I would need a bit more for scrollbars, this is a proof of concept).

Live view is very fast at re-rendering the table on scroll events.

2
Post #12
harmon25

harmon25

I think the viewport / sliding window approach you have landed on is nice!

Pretty sure it’s the only graceful way to handle this without too much data on either the client, or the server…

0
Post #13
50kudos

50kudos

That’s what I said earlier!

Is your app server in the same country or region? It’s nice to hear that rendering per scroll event is fast (I assume do_fetch_data hit database?), I’d love to adopt this in my app as well, I was afraid of latency in worst case scenario (e.g. EU ↔ NZ).

0
Post #14
kuon

kuon OP

Yeah, I didn’t want to smart render only the required region because I was surprised at how good browser were at handling large table “out of the box”, but sadly it doesn’t play well with live view diffing mechanism.

As for latency, yeah, the server is located on the same country. I guess the only way to cope with larger latency would be to send more data to the client at once, and to scroll client side within that “larger data”.

1
Post #15
david

david

I found this tutorial helpful for those looking to work with temporary assigns:
Updating and Deleting from Temporary Assigns in Phoenix LiveView. It’s a bit older but it checks out.

0
Post #16
adkron

adkron

I’m currently running into the same issue. I have a table with some data, but not a large amount. If I get over 100 rows it starts to slow down a lot. I’m looking to put 2000 rows. I would really like to have all of that on one page.

It is slow even on the initial rendering of the live view. Outside of updating does anyone know how to just render it quickly? It all seems to be rendering time that is killing it all.

Does anyone have any idea?

0
Post #17
adkron

adkron

Oh, I just learned about profiling with live view.

This might help me find it.

2
Post #18
— All posts loaded —