PolymerElements / iron-list

Element for a virtual, "infinite" list
https://www.webcomponents.org/element/PolymerElements/iron-list
219 stars 131 forks source link

When lazy loading items from a server, I want to show an item placeholder until the requested item is available #481

Open Legioth opened 7 years ago

Legioth commented 7 years ago

I want to use <iron-list> with "millions" of items from that are fetched lazily from the server whenever the user scrolls close to the boundary of what's currently available, but what I want to avoid is the phenomenon seen in e.g. https://www.webcomponents.org/element/PolymerElements/iron-scroll-threshold/demo/demo/document.html that momentum scrolling stops while waiting for more items.

Instead, I'd like to initialize <iron-list> with scrollbars based on the actual number of backend items (or an approximation thereof). If the user scrolls to a region for which there are no items, I'd like to show placeholders for the items that are currently being fetched, similarly to how e.g. <vaadin-grid> with a dataProvider shows empty rows.

I can set the length of the array based on the total item count, but only populate values for the indices that have actually been fethed. The problem is that <iron-list> ignores the item if items[idx] is null-ish. For small-ish data sets, I can work around this by eagerly populating all indices in items with the same placeholder item, but that's not practical for larger data sets.

I have two different ideas for how this could be done, each with their own pros and cons:

  1. Add a new property, e.g. placeholderItem that is used instead of a null-ish value retrieved from items.
  2. Add support for a special empty template (e.g. <template class="empty"><my-spinner></my-spinner></template> that is used instead of the regular template when getting a null.

placeholderItem would seem more convenient if I want to show the same structure for missing items, just with empty values. A custom template is instead easier to use if I want to use some completely different HTML structure. A different structure can also be achieved with placeholderItem with the use of dom-if in the regular template. It also seems like placeholderItem can be implemented quite easily, whereas introducing another template would be a quite complicated affair.

Westbrook commented 7 years ago

I've worked with the idea of the placeholderItem in the form of defaultItem before in a branch of my own and offered this PR https://github.com/PolymerElements/iron-list/pull/342 to support a similarly written issue https://github.com/PolymerElements/iron-list/issues/244.

The code therein was for the 1.x branch, but you can find the 2.x version of the same updates at https://github.com/Westbrook/iron-list/tree/2.0-ps if there was some new traction behind adding this to the feature set, I'd be happy to do some further clean up and enter a PR around the functionality.

ernsheong commented 6 years ago

@Legioth I am able to achieve this effect by creating my own custom element to act as the row item. Within the row item custom element, if the backing data object is empty, I render a placeholder view (just a dom-if).

To make this work, you need to precompute the total number of rows (say 200), i.e. let the server tell you that. Then for your first batch of items loaded from the server (say page size of 20), you need to generate 180 empty objects and populate it within the full items array. Hence the iron-list will receive an array of 200 items (20 real and 180 dummy). As the user scrolls down, install an event listener (throttled) to read the lastVisibleIndex and determine if we need to fetch more items (maintain a variable to hold the next index to load, i.e. 21). When new items do come in, we have to splice the new items in to prevent the iron-list from doing a full list and scroll reset (use this.splice or this.notifySplices)

This works really well in my case. www.pagedash.com (Check it out in production!) It's an alternative to Evernote Web Clipper.

Legioth commented 6 years ago

@ernsheong That approach works nicely when you have 200 items all in all and need to generate and populate 180 empty objects. It breaks down when you have 10 000 items and need to generate and populate 9 980 instances.

ernsheong commented 6 years ago

At that level it might make sense to "cheat" and generate more as the user gets closer, but yeah it depends on what the application requirements are.