A forrest of Lists, Grids and Repeaters

50 views
Skip to first unread message

Ruben Vreeken

unread,
Jul 21, 2015, 1:59:44 PM7/21/15
to enyo-dev...@googlegroups.com
Over the past few years enyo has gotten to a point where there are a LOT of list implementations, each with their own pro's and cons, most of them re-inventing similar wheels in different ways.

Has there been any effort to try and bring some structure to this wild and colorful gang of repeaters? Is there anything I can do to try and help bring some more structure to them?

Perhaps it would be possible to work towards a more unified structure where separate components or mixins exist for very specific functionality, such that they can be combined and composed into the more complex variety of grids and lists that exist at the moment.

For example, an item in a list would be no more than:
- An index that's unique within the confines of the list.
- An optional dataset associated with that index.

The responsibility of a page would then be to:
- receive an index and optional data for one or more items.
- create/destroy control instances for these items.
- arrange the control instances according to a particular layout strategy (arrangements should be interchangable, like DataList's delegates or panels' Arrangers).
- communicate to it's owner (the list) when it is 'full' and can no longer receive more items. (this could be at a fixed nr of items, or when the layout reaches a particular heigt, etc.)

The responsibility of the repeater could be to:
- expose an api for an adapter to supply it with data (different adapters could be implemented for Flux data, enyo.Collections, POJO's or even just a counter for the number of items).
- create and manage its own index of items
- instantiate one or more pages (as needed)
- assign items to pages (append items to a page until it's full, then create a new page for the next item).

How well would something like this fit with the new additions?

Gray Norton

unread,
Jul 21, 2015, 3:47:22 PM7/21/15
to enyo-dev...@googlegroups.com
Hi, Ruben -

Yes, the intention is to converge on a single set of repeater / list code — specifically, on the newest implementation that’s a work in progress for Enyo 2.6 (VirtualDataRepeater / NewDataList / Scrollable mixin / etc.). Names (especially NewDataList) subject to change, of course.

The way that the new implementation works is a bit different from previous generations in that it no longer uses pages at all; it positions individual items instead.

The basic division of labor is as follows:
  • DataRepeater is essentially the same as in Enyo 2.5 and earlier except that it will now accept either POJOs or Enyo collections and models.
  • VirtualDataRepeater is a subkind of DataRepeater that creates items for some contiguous subset of the full data set (N items, starting from index M) and manages the lifecycle of items as that subset changes. It knows nothing about positioning.
  • NewDataList is a subkind of VirtualDataRepeater that handles layout and scrolling. It calculates the list dimensions, processes touch and scroll wheel events (which it does by including the new Scrollable mixin), tweaks the inherited VirtualDataRepeater params (based on list metrics and current scroll coordinates) to control which items are currently in the DOM, and positions list items. Currently it is using a single, built-in layout strategy that handles vertical, horizontal and grid layouts — equivalent to the previous-generation DataList and DataGridList, except that it only supports fixed-size items at the moment — but this will change soon. I’m in the process of implementing support for variable-size items, and at the same time doing some light refactoring to make it easy to swap in different layout strategies. My vision is that these layout strategies will be usable by other types of virtualized views as well, not just data-driven lists — so you might have a scrolling view containing components that are managed using the normal Enyo APIs for adding / removing, etc., but the view would only render a subset of its components based on the current scroll coordinates.
The new implementation seems to perform well and has proven itself to be pretty versatile — besides basic list scenarios, we’ve been able to use it as the basis for a fancy TV grid and a CoverFlow-style animated list.

There are some fancy features in the first-generation Enyo list (drag-and-drop to reorder, etc.) that will need to be re-added, but I think the page-less approach of the new list will actually make it much easier to do stuff like this.

It hasn’t been a priority to do so yet, but I think we could also either a) Tweak DataRepeater to support an imperative API along the lines of the first-generation Enyo repeater and list, or b) Factor things such that DataRepeater can be swapped out for a separate, imperatively driven repeater. I’d be curious to know whether anyone has a strong preference for the imperative style, or use cases where it’s clearly better than the data-driven style.

Happy to answer questions, hear suggestions and discuss further.

- G

--
You received this message because you are subscribed to the Google Groups "Enyo Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to enyo-developme...@googlegroups.com.
To post to this group, send email to enyo-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/enyo-development/9f9f059a-3980-4736-9d32-84461e2bf53b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ruben Vreeken

unread,
Jul 21, 2015, 5:53:35 PM7/21/15
to Enyo Development
Hi Gray,

That actually sounds pretty cool! After my initial post I was actually thinking if pages would be needed, and how removing the pages would probably enable things like 'masonry' style grids.

I'm definitely +1 on the idea to factor layout strategies out of the DataList itself even more so when you say you'd like to make them available to other virtualized views as well. That sounds very cool indeed. 

It would be even cooler if the arrangement of items and the virtualization of items could be somehow decoupled. For instance, should you really need to implement the same arrangement logic for a non-virtualized set of enyo.Panels and a virtualized enyo.DataList?
If things can be kept simple and then transparantly scale up as more complex solutions are needed that would be nice. You don't really need a virtualized asynchronously rendering magic box when you're only displaying a grid of 12 items, after all.

The imperative style is mostly useful when you actually don't have a mutating list of data objects and an indexed lsit of item is really all you care about. Another situation where an imperative api could come in handy is when you want to prefix or suffix a list of items with a control that isn't necessarily bound to a record in your dataset or if you want to pad a grid with placeholder items. In the case of padding you might even want some access to the layout strategy to determine how many items you want to add (for instance, to pad the last row of a fluid grid with placeholders).

By the way, are there any plans to play around with the asynchronous rendering behaviour of the DataRepeater? I remember having some issues working with responsive layouts and grids with asynchronous rendering some time ago. It would be cool if rendering & layout behaviour could be configured to always be synchronous or asynchronous by default and then add an option to force a synchronous render/layout pass when needed.

Finally, about the scrollable mixin, is it intended to eventually replace enyo.Scroller? It would be nice if the scroll state could be observed or if drag events could be forwarded to the scroller from other controls. This would make it easy to write decorators for any scroller to implement any form of additional UI controls. For instance, we've built a decorator for enyo.Scroller to implement a fully functional scrollbar with draggable thumb as well as a scrollable content-area that adds read-more and back-to-top buttons as needed.
Currently, enyo.Scroller always responds to drag input by scrolling into one predefined direction, but it would be better if you could determine which direction it should move. For instance, a scrollbar thumb gets dragged down to scroll down, but the content inside the scroller itself is dragged up to scroll down.

I hope this reply doesn't make the scope of the conversation too wide...

--
Ruben Vreeken

Gray Norton

unread,
Jul 21, 2015, 7:26:36 PM7/21/15
to enyo-dev...@googlegroups.com
Hi, Ruben -

It would be even cooler if the arrangement of items and the virtualization of items could be somehow decoupled. For instance, should you really need to implement the same arrangement logic for a non-virtualized set of enyo.Panels and a virtualized enyo.DataList?
If things can be kept simple and then transparantly scale up as more complex solutions are needed that would be nice. You don't really need a virtualized asynchronously rendering magic box when you're only displaying a grid of 12 items, after all.

Yes, this should be doable. I haven’t worked out the details yet, but I think the API for a layout strategy can be such that you can just ignore the virtualization bits if you don’t need them.

The imperative style is mostly useful when you actually don't have a mutating list of data objects and an indexed lsit of item is really all you care about. Another situation where an imperative api could come in handy is when you want to prefix or suffix a list of items with a control that isn't necessarily bound to a record in your dataset or if you want to pad a grid with placeholder items. In the case of padding you might even want some access to the layout strategy to determine how many items you want to add (for instance, to pad the last row of a fluid grid with placeholders).

Thanks, this is helpful input. I may ask more specific questions at some point, though it may be a while as the imperative API is not (yet) at the top of the priority list.

By the way, are there any plans to play around with the asynchronous rendering behaviour of the DataRepeater? I remember having some issues working with responsive layouts and grids with asynchronous rendering some time ago. It would be cool if rendering & layout behaviour could be configured to always be synchronous or asynchronous by default and then add an option to force a synchronous render/layout pass when needed.

Are you referring to the async behavior of DataRepeater.refresh(), or the DataList.renderDelay property and related logic? NewDataList doesn’t carry over the renderDelay stuff, and while it does inherit from DataRepeater (via VirtualDataRepeater) it isn’t using refresh() much, so not sure this async stuff comes into play in common use cases. I would need to poke around a bit and refresh my memory to say for sure. If you happen to have a more specific description or a repro case for the issues you were seeing before, I’d be happy to look at it.

Finally, about the scrollable mixin, is it intended to eventually replace enyo.Scroller? It would be nice if the scroll state could be observed or if drag events could be forwarded to the scroller from other controls. This would make it easy to write decorators for any scroller to implement any form of additional UI controls. For instance, we've built a decorator for enyo.Scroller to implement a fully functional scrollbar with draggable thumb as well as a scrollable content-area that adds read-more and back-to-top buttons as needed.

The Scrollable mixin is designed to replace the guts of enyo.Scroller. For convenience and backward compatibility, we will probably still have an enyo.Scroller, but most of its behavior will be provided under the hood by the mixin. The main reason for factoring this way is so that a virtual list (for example) doesn’t need to inherit from a scroller or contain a scroller (which results in extra dom and some unneeded functionality); it can just “act like a scroller” in the relevant ways. Scrollable currently has what I think is a pretty clean, simple API for interfacing with swappable scroll controls, with use cases like yours in mind (though I’m not sure I understand enough about your “scrollable content-area” to say whether it fits the mold of scroll controls that the current API has in mind).

- G
Date: Tuesday, July 21, 2015 at 2:53 PM
To: Enyo Development <enyo-dev...@googlegroups.com>
Subject: Re: [enyo-dev] A forrest of Lists, Grids and Repeaters

Ruben Vreeken

unread,
Jul 22, 2015, 8:53:59 AM7/22/15
to Enyo Development

Hey Gray,

Yes, this should be doable. I haven’t worked out the details yet, but I think the API for a layout strategy can be such that you can just ignore the virtualization bits if you don’t need them.

Very awesome! Let me know if I can be of any help here.

Thanks, this is helpful input. I may ask more specific questions at some point, though it may be a while as the imperative API is not (yet) at the top of the priority list.

I'd be happy to help if you have any questions.

Are you referring to the async behavior of DataRepeater.refresh(), or the DataList.renderDelay property and related logic? NewDataList doesn’t carry over the renderDelay stuff, and while it does inherit from DataRepeater (via VirtualDataRepeater) it isn’t using refresh() much, so not sure this async stuff comes into play in common use cases. I would need to poke around a bit and refresh my memory to say for sure. If you happen to have a more specific description or a repro case for the issues you were seeing before, I’d be happy to look at it.

In my case, it was the renderDelay property and related logic. I don't recall the exact details, but one issue I recall was with a fluid grid that would display items up to a maximum column width. If a grid required scrolling, a scrollbar would be added outside the scroller. To make room for the scrollbar, the grid itself is made a little narrower. The changes in grid dimensions could trigger a reduction or increase in column count or row height at very specific resolutions. This would then cause various subtle side-effects that needed to be handled. 

To handle some of these side-effects, we needed to synchronously reflow the grid and get some up-to-date metrics. Through mechanics I don't entirely recall, it turned out DataList.getBounds() was not always returning the desired metrics. Apparently the new grid layout was not - or not always - resolved synchronously. The solution we ended up with was to explicitly trigger a reflow before getting the DataLists' bounds. It's an ugly hack but it worked for our particular case. Either way, considering the complexity of the DataList implementation, it took a lot of time to work out what was going on exactly and simply having the choice to switch between an asyncronous and synchronous mode would have been much simpler.

The Scrollable mixin is designed to replace the guts of enyo.Scroller. For convenience and backward compatibility, we will probably still have an enyo.Scroller, but most of its behavior will be provided under the hood by the mixin. The main reason for factoring this way is so that a virtual list (for example) doesn’t need to inherit from a scroller or contain a scroller (which results in extra dom and some unneeded functionality); it can just “act like a scroller” in the relevant ways. Scrollable currently has what I think is a pretty clean, simple API for interfacing with swappable scroll controls, with use cases like yours in mind (though I’m not sure I understand enough about your “scrollable content-area” to say whether it fits the mold of scroll controls that the current API has in mind).

The scrollable content area isn't very complicated. It's literally a scroller with a max-height. The whole thing is then wrapped with a decorator that adds a "read-more" button at the bottom that can be pressed and held to scroll down until the bottom is reached, at which point the button is removed. Similarly, a scroll-to-top button is revealed when the content is not already fully scrolled to the top.


I'll try to make some time to really dive into the new code to get a better grasp on what's going on exactly.

--
Ruben Vreeken
Reply all
Reply to author
Forward
0 new messages