dhilt / ngx-ui-scroll

Virtual/infinite scroll for Angular
https://dhilt.github.io/ngx-ui-scroll/
MIT License
224 stars 18 forks source link

Add blank rows while fetching new items #65

Open alexus85 opened 6 years ago

alexus85 commented 6 years ago

Would it be possible to add blank rows of expected quantity (before dispatching the request) instead of increasing the gap in top or bottom part of the list when scrolling? It's really annoying to see the blank space while waiting for the data to be loaded, if the above was possible, we could show blank rows (with a spinner for example) and once the data are loaded we can then populate the pre-generated rows with the loaded data.

What do you think?

dhilt commented 6 years ago

I see, you want some stub rows to be inserted into the view before the Datasource.get call. This feature definitely could be implemented, and I've already thought about it. There are some difficulties with uncertain heights case... So putting the "enhancement" label for now.

avmaxim commented 2 years ago

@dhilt Hi! Is there any progress on the encanchement above or any thoughts on adding it to the future versions? We have a large dataset in the app and it's also annoying to see the blank rows and lags while scrolling the data back and forth

dhilt commented 2 years ago

@avmaxim Unfortunately I had no time for this... One thing just came to my mind, you may try the following workaround:

div[data-padding-backward], div[data-padding-forward] {
  background-image: url("scr-row.png");
  background-repeat: repeat-y;
}

Here scr-row.png is an image of two empty rows. It should work a little bit better if your template consists of only 1 row (with no difference between odd/even).

Aug-21-2022 23-57-48

avmaxim commented 2 years ago

@dhilt Thanks! It partly works. BUT with this approach at least a few critical downsides that lead to additional problems seem to loom:

  1. In case a number of visible items is less than a maximum number of visible items in the viewable area (like we see 9 items, but we have only 3 to show), we can't rely on [data-padding-backward] and [data-padding-forward] directives because the available space that is left will be also affected by those directives and we will always see the repeated preloaders / placeholders in the bottom
  2. In case we want to implement something different than an image - let's say a CSS animation shimmer to show an animated skeleton preloader to the user - we won't be able to repeat it across the whole viewable area same as we can to with an image.

Please advice how we can alliviate these bottlenecks. Thank you!

dhilt commented 2 years ago

@avmaxim Well, I believe this is not a good game, but the 1 problem could be solved via manipulating styles in runtime. The approach can be improved and adapted to the application/view needs, but the POC is as follows:

const sub = combineLatest([
  this.datasource.adapter.eof$,
  this.datasource.adapter.isLoading$
]).subscribe(([eof, loading]) => {
  // waiting for stop in the end-of-file
  if (eof && !loading) {
    // check if there is an empty space in the visible part of the viewport
    const viewportEl = document.querySelector('.viewport') as HTMLElement;
    if (viewportEl.scrollHeight <= viewportEl.clientHeight) { // ===
      // remove background-image
      const fwdPaddingElt = document.querySelector('[data-padding-forward]') as HTMLElement;
      fwdPaddingElt.style.backgroundImage = 'none';
    }
    sub.unsubscribe();
  }
});

Instead, you may not to have styles on start, and apply background-image only when scrollHeight > clientHeight. ResizeObserver may help.


The 2 problem requires core changes... Btw, do you experience lags because of data flow latency, or is it just a matter of rendering? By data flow latency, I mean the latency at the Datasource.get level. In this case you may introduce stub items that will be pushed to the Scroller by the Datasource.get method immediately (sort of reverse cache layer) and then (when data comes) replace them with real data-items using the Adapter replace/update/etc methods.

But if the lags being discussed have render-only nature (no delay in Datasource.get), then I don't see how it can be optimized with the existing code of the Scroller. There is no re-using of the HTML containers, so when you are scrolling, elements get destroyed and then created again with reliance on browser speed. Here's the render delay introduced within the core: domRoutines.ts#L187-L190.