Skip to content

Commit f660c4c

Browse files
authored
Merge branch 'hotfix/hotfix-v28.2.3' into EJ2-performance
2 parents 799932b + 76b0fda commit f660c4c

File tree

7 files changed

+282
-16
lines changed

7 files changed

+282
-16
lines changed

ej2-asp-core-mvc/grid/EJ2_ASP.MVC/scrolling/virtual-scrolling.md

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,141 @@ The following example enable column virtualization using `EnableColumnVirtualiza
107107
9. Hierarchy grid
108108
10. Autofill
109109
11. Column chooser
110-
12. Page
110+
12. Page
111+
112+
## Browser height limitation in virtual scrolling and solution
113+
114+
You can load millions of records in the Grid by using virtual scrolling, where the Grid loads and renders rows on-demand while scrolling vertically. As a result, Grid lightens the browser’s load by minimizing the DOM elements and rendering elements visible in the viewport. The height of the Grid is calculated using the Total Records Count * [RowHeight](https://help.syncfusion.com/cr/aspnetmvc-js2/Syncfusion.EJ2.Grids.Grid.html#Syncfusion_EJ2_Grids_Grid_RowHeight) property.
115+
116+
The browser has some maximum pixel height limitations for the scroll bar element. The content placed above the maximum height can't be scrolled if the element height is greater than the browser's maximum height limit. The browser height limit affects the virtual scrolling of the Grid. When a large number of records are bound to the Grid, it can only display the records until the maximum height limit of the browser. Once the browser's height limit is reached while scrolling, the user won't able to scroll further to view the remaining records.
117+
118+
For example, if the row height is set as 30px and the total record count is 1000000(1 million), then the height of the Grid element will be 30,000,000 pixels. In this case, the browser's maximum height limit for a div is about 22,369,600 (The maximum pixel height limitation differs for different browsers). The records above the maximum height limit of the browser can't be scrolled.
119+
120+
This height limitation is not related to the Grid. It fully depends on the default behavior of the browser. The same issue is reproduced in the normal HTML table too.
121+
122+
The following image illustrates the height limitation issue of a normal HTML table in different browsers (Chrome and Firefox).
123+
124+
![Browser height limitation in HTML table](../images/scrolling/html-table.gif)
125+
126+
Grid also faced the same issue as mentioned in the below image.
127+
128+
![Grid with browser height limitation](../images/scrolling/grid.gif)
129+
130+
The Grid has an option to overcome this limitation of the browser in the following ways.
131+
132+
### Solution 1: Using external buttons
133+
134+
You can prevent the height limitation problem in the browser when scrolling through millions of records by loading the segment of data through different strategy.
135+
136+
In the following sample, Grid is rendered with a large number of records(nearly 2 million). Here, you can scroll 0.5 million records at a time in Grid. Once you reach the last page of 0.5 million records, the **Load Next Set** button will be shown at the bottom of the Grid. By clicking that button, you can view the next set of 0.5 million records in Grid. Also, the **Load Previous Set** button will be shown at the top of the Grid to load the previous set of 0.5 million records.
137+
138+
Let's see the step by step procedure for how we can overcome the limitation in the Syncfusion Grid.
139+
140+
1. Create a custom adaptor by extending `UrlAdaptor` and binding it to the Grid [DataSource](https://help.syncfusion.com/cr/aspnetmvc-js2/syncfusion.ej2.grids.grid.html#Syncfusion_EJ2_Grids_Grid_DataSource) property. In the **processQuery** method of the custom adaptor, we handled the `Skip` query based on the current page set to perform the data operation with whole records on the server.
141+
142+
```typescript
143+
class CustomAdaptor extends ej.data.UrlAdaptor {
144+
processQuery(dataManager, query) {
145+
if (query.queries) {
146+
query.queries.forEach(queryItem => {
147+
if (queryItem.fn === 'onPage') {
148+
// pageSet - Defines the number of segments going to split the 2 million records. In this example 0.5 million records are considered for each set so the pageSet is 1, 2, 3 and 4.
149+
// maxRecordsPerPageSetIn this example the value is define as 0.5 million.
150+
// gridPageSizeThe pageSize defined in the Grid as pageSettings->pageSize property.
151+
// Customize the pageIndex based on the current pageSet (It send the skip query including the previous pageSet ) so that the other operations performed for total 2 millions records instead of 0.5 million alone.
152+
let pageIndex = queryItem.e.pageIndex;
153+
pageIndex = (((pageSet - 1) * maxRecordsPerPageSet) / gridPageSize) + pageIndex;
154+
}
155+
});
156+
}
157+
return super.processQuery(dataManager, query);
158+
}
159+
}
160+
161+
document.addEventListener("DOMContentLoaded", function () {
162+
let grid = document.getElementById("Grid").ej2_instances[0];
163+
if (grid) {
164+
let dataManager = new ej.data.DataManager({
165+
url: "/api/Grid",
166+
adaptor: new CustomAdaptor()
167+
});
168+
grid.dataSource = dataManager;
169+
}
170+
```
171+
172+
2. Render the Grid by define the following features.
173+
174+
```cshmtl
175+
@Html.EJS().Grid("Grid").DataSource((IEnumerable<object>)ViewBag.dataSource).EnableVirtualization().Height("360").BeforeDataBound("beforeDataBound").Columns(col =>
176+
{
177+
col.Field("OrderID").HeaderText("OrderID").Width("100").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
178+
......
179+
......
180+
}).PageSettings(page => { page.PageSize(50); }).Render();
181+
```
182+
183+
3. In the [beforeDataBound](https://help.syncfusion.com/cr/aspnetmvc-js2/syncfusion.ej2.grids.grid.html#Syncfusion_EJ2_Grids_Grid_BeforeDataBound) event, we set the args.count as 0.5 million to perform scrolling with 0.5 million records and all the data operations are performed with whole records which is handled using the custom adaptor. And also particular segment records count is less than 0.5 million means it will directly assigned the original segmented count instead of 0.5 million.
184+
185+
```typescript
186+
function beforeDataBound(args) {
187+
// Storing the total records count which means 2 million records count.
188+
totalRecords = args.count;
189+
190+
// Change the count with respect to maxRecordsPerPageSet (maxRecordsPerPageSet = 500000).
191+
args.count = args.count - ((pageSet - 1) * maxRecordsPerPageSet) > maxRecordsPerPageSet ?maxRecordsPerPageSet : args.count - ((pageSet - 1) * maxRecordsPerPageSet);
192+
}
193+
```
194+
195+
4. Render “Load Next Set” button and “Load Previous Set” button at bottom and top of the Grid.
196+
197+
```cshtml
198+
@Html.EJS().Button("prevButton").Class("e-info prevbtn").CssClass("e-info prevbtn e-primary").Content("Load Previous Set...").Render()
199+
@Html.EJS().Grid("Grid").DataSource((IEnumerable<object>)ViewBag.dataSource).EnableVirtualization().Height("360").BeforeDataBound("beforeDataBound").Columns(col =>
200+
{
201+
col.Field("OrderID").HeaderText("OrderID").Width("100").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
202+
......
203+
......
204+
}).PageSettings(page => { page.PageSize(50); }).Render();
205+
@Html.EJS().Button("nextButton").Class("e-info nxtbtn").CssClass("e-info nxtbtn e-primary").Content("Load Next Set...").Render()
206+
```
207+
208+
5. While click on the `Load Next Set` / `Load Previous Set` button corresponding page data set is loaded to view remaining records of total 2 millions records after doing some simple calculation.
209+
210+
```typescript
211+
document.getElementById("prevButton").addEventListener("click", function () {
212+
loadPageSet(-1); // Move to the previous page set.
213+
});
214+
215+
document.getElementById("nextButton").addEventListener("click", function () {
216+
loadPageSet(1); // Move to the next page set.
217+
});
218+
219+
function loadPageSet(direction) {
220+
let grid = document.getElementById("Grid").ej2_instances[0];
221+
if (grid && grid.element) {
222+
let contentElement = grid.element.querySelector('.e-content');
223+
if (contentElement && contentElement.getAttribute('aria-busy') === 'false') {
224+
pageSet += direction; // Update page set based on direction (-1 for previous, +1 for next).
225+
grid.refresh(); // Reload data with the new page set.
226+
} else {
227+
console.warn("Grid is still loading. Please wait..."); // Prevent multiple clicks while loading.
228+
}
229+
}
230+
}
231+
```
232+
233+
![Prevent browser height limitation](../images/scrolling/external-button.png)
234+
235+
> If you perform Grid actions such as filtering, sorting, etc., after scrolling through the 0.5 million data, the Grid performs those data actions with the whole records, not just the current loaded 0.5 million data.
236+
237+
### Solution 2: Using RowHeight property
238+
239+
You can reduce the [RowHeight](https://help.syncfusion.com/cr/aspnetmvc-js2/Syncfusion.EJ2.Grids.Grid.html#Syncfusion_EJ2_Grids_Grid_RowHeight) using the `RowHeight` property of the Grid. It will reduce the overall height to accommodate more rows. But this approach optimizes the limitation, but if the height limit is reached after reducing row height also, you have to opt for the previous solution or use paging.
240+
241+
In the following image, you can see how many records will be scrollable when setting `RowHeight` to "36px" and "30px".
242+
243+
![Row Height](../images/scrolling/row-height.gif)
244+
245+
### Solution 3: Using paging instead of virtual scrolling
246+
247+
Similar to virtual scrolling, the [Paging](https://ej2.syncfusion.com/aspnetmvc/documentation/grid/paging/) feature also loads the data in an on-demand concept. Pagination is also compatible with all the other features(Grouping, Editing, etc.) in Grid. So, use the `paging` feature instead of virtual scrolling to view a large number of records in the Grid without any kind of performance degradation or browser height limitation.

ej2-asp-core-mvc/grid/EJ2_ASP.NETCORE/scrolling/virtual-scrolling.md

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,145 @@ The following example enable column virtualization using `enableColumnVirtualiza
108108
9. Hierarchy grid
109109
10. Autofill
110110
11. Column chooser
111-
12. Page
111+
12. Page
112+
113+
## Browser height limitation in virtual scrolling and solution
114+
115+
You can load millions of records in the Grid by using virtual scrolling, where the Grid loads and renders rows on-demand while scrolling vertically. As a result, Grid lightens the browser’s load by minimizing the DOM elements and rendering elements visible in the viewport. The height of the Grid is calculated using the Total Records Count * [rowHeight](https://help.syncfusion.com/cr/aspnetcore-js2/Syncfusion.EJ2.Grids.Grid.html#Syncfusion_EJ2_Grids_Grid_RowHeight) property.
116+
117+
The browser has some maximum pixel height limitations for the scroll bar element. The content placed above the maximum height can't be scrolled if the element height is greater than the browser's maximum height limit. The browser height limit affects the virtual scrolling of the Grid. When a large number of records are bound to the Grid, it can only display the records until the maximum height limit of the browser. Once the browser's height limit is reached while scrolling, the user won't able to scroll further to view the remaining records.
118+
119+
For example, if the row height is set as 30px and the total record count is 1000000(1 million), then the height of the Grid element will be 30,000,000 pixels. In this case, the browser's maximum height limit for a div is about 22,369,600 (The maximum pixel height limitation differs for different browsers). The records above the maximum height limit of the browser can't be scrolled.
120+
121+
This height limitation is not related to the Grid. It fully depends on the default behavior of the browser. The same issue is reproduced in the normal HTML table too.
122+
123+
The following image illustrates the height limitation issue of a normal HTML table in different browsers (Chrome and Firefox).
124+
125+
![Browser height limitation in HTML table](../images/scrolling/html-table.gif)
126+
127+
Grid also faced the same issue as mentioned in the below image.
128+
129+
![Grid with browser height limitation](../images/scrolling/grid.gif)
130+
131+
The Grid has an option to overcome this limitation of the browser in the following ways.
132+
133+
### Solution 1: Using external buttons
134+
135+
You can prevent the height limitation problem in the browser when scrolling through millions of records by loading the segment of data through different strategy.
136+
137+
In the following sample, Grid is rendered with a large number of records(nearly 2 million). Here, you can scroll 0.5 million records at a time in Grid. Once you reach the last page of 0.5 million records, the **Load Next Set** button will be shown at the bottom of the Grid. By clicking that button, you can view the next set of 0.5 million records in Grid. Also, the **Load Previous Set** button will be shown at the top of the Grid to load the previous set of 0.5 million records.
138+
139+
Let's see the step by step procedure for how we can overcome the limitation in the Syncfusion Grid.
140+
141+
1. Create a custom adaptor by extending `UrlAdaptor` and binding it to the Grid [DataSource](https://help.syncfusion.com/cr/aspnetcore-js2/syncfusion.ej2.grids.grid.html#Syncfusion_EJ2_Grids_Grid_DataSource) property. In the **processQuery** method of the custom adaptor, we handled the `Skip` query based on the current page set to perform the data operation with whole records on the server.
142+
143+
```typescript
144+
class CustomAdaptor extends ej.data.UrlAdaptor {
145+
processQuery(dataManager, query) {
146+
if (query.queries) {
147+
query.queries.forEach(queryItem => {
148+
if (queryItem.fn === 'onPage') {
149+
// pageSet - Defines the number of segments going to split the 2 million records. In this example 0.5 million records are considered for each set so the pageSet is 1, 2, 3 and 4.
150+
// maxRecordsPerPageSetIn this example the value is define as 0.5 million.
151+
// gridPageSizeThe pageSize defined in the Grid as pageSettings->pageSize property.
152+
// Customize the pageIndex based on the current pageSet (It send the skip query including the previous pageSet ) so that the other operations performed for total 2 millions records instead of 0.5 million alone.
153+
let pageIndex = queryItem.e.pageIndex;
154+
pageIndex = (((pageSet - 1) * maxRecordsPerPageSet) / gridPageSize) + pageIndex;
155+
}
156+
});
157+
}
158+
return super.processQuery(dataManager, query);
159+
}
160+
}
161+
162+
document.addEventListener("DOMContentLoaded", function () {
163+
let grid = document.getElementById("Grid").ej2_instances[0];
164+
if (grid) {
165+
let dataManager = new ej.data.DataManager({
166+
url: "/api/Grid",
167+
adaptor: new CustomAdaptor()
168+
});
169+
grid.dataSource = dataManager;
170+
}
171+
```
172+
173+
2. Render the Grid by define the following features.
174+
175+
```cshtml
176+
<ejs-grid id="Grid" enableVirtualization="true" height="300" beforeDataBound="beforeDataBound">
177+
<e-grid-pageSettings pageSize=50></e-grid-pageSettings>
178+
<e-grid-columns>
179+
<e-grid-column field="OrderID" headerText="Order ID" width="120" textAlign="Right" isPrimaryKey="true" type="number"></e-grid-column>
180+
......
181+
......
182+
</e-grid-columns>
183+
</ejs-grid>
184+
```
185+
186+
3. In the [beforeDataBound](https://help.syncfusion.com/cr/aspnetcore-js2/syncfusion.ej2.grids.grid.html#Syncfusion_EJ2_Grids_Grid_BeforeDataBound) event, we set the args.count as 0.5 million to perform scrolling with 0.5 million records and all the data operations are performed with whole records which is handled using the custom adaptor. And also particular segment records count is less than 0.5 million means it will directly assigned the original segmented count instead of 0.5 million.
187+
188+
```typescript
189+
function beforeDataBound(args) {
190+
// Storing the total records count which means 2 million records count.
191+
totalRecords = args.count;
192+
193+
// Change the count with respect to maxRecordsPerPageSet (maxRecordsPerPageSet = 500000).
194+
args.count = args.count - ((pageSet - 1) * maxRecordsPerPageSet) > maxRecordsPerPageSet ?maxRecordsPerPageSet : args.count - ((pageSet - 1) * maxRecordsPerPageSet);
195+
}
196+
```
197+
198+
4. Render “Load Next Set” button and “Load Previous Set” button at bottom and top of the Grid.
199+
200+
```cshtml
201+
<ejs-button id='prevButton' class="e-info prevbtn" cssClass='e-primary' content="Load Previous Set.."></ejs-button>
202+
<ejs-grid id="Grid" enableVirtualization="true" height="300" beforeDataBound="beforeDataBound">
203+
<e-grid-pageSettings pageSize=10></e-grid-pageSettings>
204+
<e-grid-columns>
205+
<e-grid-column field="OrderID" headerText="Order ID" width="120" textAlign="Right" isPrimaryKey="true" type="number"></e-grid-column>
206+
......
207+
......
208+
</e-grid-columns>
209+
</ejs-grid>
210+
<ejs-button id='nextButton' class="e-info nxtbtn" cssClass='e-primary' content="Load Next Set..."></ejs-button>
211+
```
212+
213+
5. While click on the `Load Next Set` / `Load Previous Set` button corresponding page data set is loaded to view remaining records of total 2 millions records after doing some simple calculation.
214+
215+
```typescript
216+
document.getElementById("prevButton").addEventListener("click", function () {
217+
loadPageSet(-1); // Move to the previous page set.
218+
});
219+
220+
document.getElementById("nextButton").addEventListener("click", function () {
221+
loadPageSet(1); // Move to the next page set.
222+
});
223+
224+
function loadPageSet(direction) {
225+
let grid = document.getElementById("Grid").ej2_instances[0];
226+
if (grid && grid.element) {
227+
let contentElement = grid.element.querySelector('.e-content');
228+
if (contentElement && contentElement.getAttribute('aria-busy') === 'false') {
229+
pageSet += direction; // Update page set based on direction (-1 for previous, +1 for next).
230+
grid.refresh(); // Reload data with the new page set.
231+
} else {
232+
console.warn("Grid is still loading. Please wait..."); // Prevent multiple clicks while loading.
233+
}
234+
}
235+
}
236+
```
237+
238+
![Prevent browser height limitation](../images/scrolling/external-button.png)
239+
240+
> If you perform Grid actions such as filtering, sorting, etc., after scrolling through the 0.5 million data, the Grid performs those data actions with the whole records, not just the current loaded 0.5 million data.
241+
242+
### Solution 2: Using RowHeight property
243+
244+
You can reduce the [rowHeight](https://help.syncfusion.com/cr/aspnetcore-js2/Syncfusion.EJ2.Grids.Grid.html#Syncfusion_EJ2_Grids_Grid_RowHeight) using the `rowHeight` property of the Grid. It will reduce the overall height to accommodate more rows. But this approach optimizes the limitation, but if the height limit is reached after reducing row height also, you have to opt for the previous solution or use paging.
245+
246+
In the following image, you can see how many records will be scrollable when setting `rowHeight` to "36px" and "30px".
247+
248+
![Row Height](../images/scrolling/row-height.gif)
249+
250+
### Solution 3: Using paging instead of virtual scrolling
251+
252+
Similar to virtual scrolling, the [paging](https://ej2.syncfusion.com/aspnetcore/documentation/grid/paging/) feature also loads the data in an on-demand concept. Pagination is also compatible with all the other features(Grouping, Editing, etc.) in Grid. So, use the `paging` feature instead of virtual scrolling to view a large number of records in the Grid without any kind of performance degradation or browser height limitation.
Loading
34.3 KB
Loading
Loading
Loading

0 commit comments

Comments
 (0)