What is Continuous Virtualization in the jQuery Grids?

Damyan Petev / Wednesday, April 4, 2012

Get the lowdown on the virtualization that never stops, so to speak… Soon enough the countdown to 12.1 will be over and what you will find named as Continuous Virtualization is an awesome new feature. It is designed specifically for hierarchical data – therefore finds it’s place as feature for our powerful jQuery Grids, more specifically providing virtualization for hierarchical and grouped layouts. We are looking forward to share all the new goodness that is ahead and we want you to be as excited about them as we are! So, to give you a sneak peak, lets talk virtualization. Of course, you would need some basics on the virtualization process the grids offer currently.

Virtual you say?

When performance is your goal, virtualization is pretty much your best friend. It is an unique feature of the client jQuery grid and it can greatly speed up not just the initial loading but the overall processes on the client. The idea is really simple on paper, much harder to do right and most importantly highly effective – you can have a million records in the data, but there’s no point to create UI elements for them all when the UI can only show a tiny(in comparison) amount. So instead the grid will only create pre-defined number of rows and use them to display the visible data. That in essence means two simple things – the footprint of that huge data will be actually really small and it would be about the same for various sizes of records and therefore the memory required, the load on the client (CPU usage) for the UI will not only be low, but should also remain almost the same with smaller and larger sets of data.

So, you might wonder, what happens when you do want to see those rows that have not been included? Here is where the virtualization really shines – among other possible routes of implementing it, we chose to keep that pool of rows completely static. That means the grid will not just use them – it will reuse them. Which means that those actual UI elements are not going anywhere. As the user scroll down the grid will instead replace data in the rows rather than deleting them and making new ones (which once more saves time and consumption). The rows also stay static in the DOM which is yet another benefit, as moving items in the DOM is also relatively slow process.

While all this is wonderful, there are some limitations currently – this virtualization cannot work with rows that have variable height or such that are being moved or expanded. That is essence excludes the Hierarchical Grid and the layout created by the Group By feature.

Filling the gaps

The Continuous Virtualization comes to make up for the shortfalls of the otherwise brilliantly working with flat data virtualization. Again it uses a predefined number of rows and as the user scrolls it will determine if the available rows are enough to display the ones in view are react accordingly, which again ends up with significantly reduced overhead in the DOM as just a small portion of rows, compared to the data, will exist there. There’s more – to accommodate variable row heights, after a scroll takes place the grid needs to calculate which rows should be displayed and it does that by calculating an average for the height – which makes the AvgRowHeight, that is pretty much required by the fixed virtualization, a thing of the past. Calculation is based of the actual rows the grid has available (the ones that are/were visible and created) and it is logically only an approximation rather than the true average value. That can cause same issues like incorrect scroller position and to prevent that the grid is smart enough and will adjust that positing on the fly, if such event occurs. Of course, the pool of rows will serve as a buffer for options such as a row being expanded, but if the scrolling process ‘pushes’ it out, that row will be set back to it’s default.

So besides  the fact that it completes the functionality of the grids, why is it continuous? The point it that it doesn’t virtualize a pre-defined on load layout, but rather dynamic layouts that don’t exist quite yet – like a child of an expanded row and expandable new parent-rows as a result of grouping. It’s a process that just keeps ticking – constantly ready to provide virtualization and constantly re-calculating row heights and adjusting the scrollbar to provide you that seamless scrolling experience with hierarchical data of impressive sizes.

Let’s jump into an actual example and grab the all-time-favourite Nothwind and lets have our Customers information from there displayed neatly on the client by our NetAdvantage for jQuery Grid and since we are at it lets enable its GroupBy feature so we can have a look at our customers from each country for example. To take benefits from the new virtualization option we need to do some settings:

-First and foremost – enable virtualization

- Set the virtualization mode to continuous

- Define width and height for the grid – This is required for virtualization.

  1. $("#grid").igGrid({
  2.     // required for virtualization
  3.     height: "300px",
  4.     width: "700px",
  5.     // enable virtualization
  6.     virtualization: true,
  7.     // --
  8.     //  set to continuous
  9.     virtualizationMode: "continuous",
  10.     // --
  11.     autoGenerateColumns: true,
  12.     dataSource: "/Home/Customers",
  13.     features: [
  14.         { name: "GroupBy" }]
  15. });

That code essentially combined with a server-side JSON-returning action makes for an extremely fast and easy delivered app with a client grid that supports grouping and you can pretty much do this:

igGrid with Group By Continuous Virtualization

If you look at the generated markup you get 54 rows :

igGrid with Group By Continuous Virtualization - produced markup

Since the Northwind customers are only 90-something number like that isn’t all that impressive and frankly you can go without virtualization as whole. Then again those rows also contain the group header rows that are not really part of the data, but rather added by the GroupBy feature and still they are visible, thus they are in the markup. Still say you have a few of them in those fifty rows in the DOM – that’s still probably over one third of the actual data in there?

BUT.. things get drastically better when you go for the big data. No matter how much - from 500 to 10000 -  when you look at the markup you will see this:

igGrid with Group By Continuous Virtualization - produced markup with 10000 rows

Surprised? I hope not – that was the whole point of virtualizing the UI elements in the DOM! You get a constant number of generated actual rows, unaffected by the size of your data. And what happens when you scroll to the bottom of a 10 000 ‘rows’? Do all the stuff you scroll through get added to those in the DOM? Of course not! Look:

igGrid with Group By Continuous Virtualization - produced markup, 10000 rows after scrolling

Note that while the IDs of the rows are now in the 8000 range, but if you look closely: 8541 minus 8489 is.. well what a surprise – 52(or 53 rows)! The row count stays the same as it not affected by the position or the number of rows that have been displayed already – just like it is explained – the grid will simply keep a constant pool of reusable rows and it is not based on the data, as records come and go for those rows – it is actually based on the height of the layout!

With even more hierarchy

Now when you want a hierarchical grid this new virtualization does not only allow for the expandable rows to be virtualized. It does not only allow for them to have variable height and it does not only function with Group By. Continuous virtualization can be set for child layouts. Here’s a sample grid definition, this time using both the Customers and their orders:

 

  1. $("#grid").igHierarchicalGrid({
  2.     // required for virtualization
  3.     height: "300px",
  4.     width: "700px",
  5.     // enable virtualization
  6.     virtualization: true,
  7.     // --
  8.     //  set to continuous
  9.     virtualizationMode: "continuous",
  10.     // --
  11.     autoGenerateColumns: false,
  12.     autoGenerateLayouts: false,
  13.     initialDataBindDepth: 1,
  14.     primaryKey: "CustomerID",
  15.     defaultChildrenDataProperty: "Orders",
  16.     dataSource: "/Home/CustomersAndOrders",
  17.     columns: [
  18.                 { headerText: "Customer ID", key: "CustomerID", dataType: "string" },
  19.                 { headerText: "Company Name", key: "CompanyName", dataType: "string" },
  20.                 { headerText: "City", key: "City", dataType: "string" },
  21.                 { headerText: "Country", key: "Country", dataType: "string" }
  22.         ],
  23.     columnLayouts: [{
  24.         key: "Orders",
  25.         childrenDataProperty: "Orders",
  26.         autoGenerateColumns: false,
  27.         autoGenerateLayouts: false,
  28.         primaryKey: "OrderID",
  29.         // ## BEGIN
  30.         // enable virtualization
  31.         virtualization: true,
  32.         // --
  33.         //  set to continuous
  34.         virtualizationMode: "continuous",
  35.         // ## END
  36.         width: "300px",
  37.         height: "50px",
  38.         columns: [
  39.                 { headerText: "Order ID", key: "OrderID", dataType: "number" },
  40.                 { headerText: "Freight", key: "Freight", dataType: "number" }
  41.             ],
  42.         features: [
  43.                 { name: "GroupBy",
  44.                   tyoe: "local"
  45.                  }
  46.             ]
  47.     }],
  48.     features: [
  49.         { name: "GroupBy",
  50.             type: "local"
  51.         },
  52.         { name: "Sorting",
  53.             type: "local"
  54.         }
  55.         ]
  56. });

This allows for each grid instance to have it's own virtualization - parent, child or child that has its own hierarchy and so on.

You might notice there’s a height of 50 defined for the child layout (as with the parent, that is required for virtualization) – that is because the number of orders just isn’t enough and since the pool of rows is based on height I had to squish it a little to force the process. Than again that is just an example (expect better ones soon) and just because of that if we take the customer with ID BONAP with just 17 orders (the most a quick look managed). When you expand that row the child layout’s markup will consist of just 10 rows. If the height is about 100 the rows would be about 20 regardless if you have 100 or 1000 records in there or they have child records themselves – and that’s, simply put, overhead reduction!

The advantage you get

No matter the size, whether it’s 100 or 10 000, the DOM structure on the client side will remain stable and most importantly low. Now imagine that was not available, imagine hierarchical structure of 10 000 rows with 100 more children each… that’s 10 000 TR elements alone and then imagine the cells in them - a certain recipe for disaster! And believe me, I do mean it – a sample with generated just as much data I tried  basically made attempt to crash my browsers and while they managed – the performance was unimpressive – expanding a row (additional 100 rows) and initial loading took some time. So whenever you can’t rely on loading data on demand (or that data is so much that each piece if that kind of size) you can enable the Continuous virtualization feature and enjoy instant loading and expanding and fluid interaction with huge data. This feature does not only reduce the memory load and CPU consumption, it actually makes such high numbers feasible option.

That’s no small thing to be looking for – as soon as the new release is a fact I will add a demo project to this for you to download. Last but not least, expect more great additions – so keep eye on the community site, follows @Infragistics, give a like on Facebook and we will do our best to keep you informed, prepare you for the new stuff and hopefully have you thrilled about it!

UPDATE: Demo project available for download! Including both flat data grid virtualization in combination with grouping, as well as hierarchical grid implementation.