I am rewriting a site from Blazor Server to Blazor Web Assembly due to some performance/responsiveness issues.
The same data with a more complex pivot table (many more cell templates) is MUCH slower client side. My client is dealing with fewer pieces of data than your demo posted here: https://blazor.syncfusion.com/demos/pivot-table/drill-through?theme=bootstrap5 so I feel there must be something wrong.
The data population completes in about 2 seconds. The pivot grid then takes 10+ seconds to render.
Any help would be appreciated!
@page "/ProductionBottleneck" @using EM3.Client.Services @using EM3.Shared.Models; @using EM3.Shared.Models.ProductionBottleneck; @using System.Collections.ObjectModel @using DevExpress.Blazor <div> <SfDropDownList @ref="locationDropDown" TValue="IdNameDto" TItem="IdNameDto" @bind-Value="selectedLocation" Placeholder="Select location" Width="300px" DataSource="Services.EMService.LocationsCacheWithAny" IndexChanged="ReconfigureAndReloadPivotTable"> <DropDownListFieldSettings Value="ID" Text="Name"></DropDownListFieldSettings> </SfDropDownList> <SfDropDownList @ref="pivotDropDown" TValue="string" TItem="string" Placeholder="Select type..." @bind-Value="selectedPivotType" Width="300px" DataSource="@(new string[] {"By Step, PM, Project", "By PM, Project", "By PM, Step", "By Labor, PM, Step", "By Labor, Step"})" IndexChanged="@(async () => await ReconfigureAndReloadPivotTable())"> <DropDownListFieldSettings Value="ID" Text="Name"></DropDownListFieldSettings> </SfDropDownList> <SfDropDownList @ref="customerDropDown" TValue="string" TItem="string" Placeholder="Select customer..." @bind-Value="selectedCustomer" Width="300px" ShowClearButton="true" DataSource="@(new string[] {"Hy-Vee", "ISEC"})" IndexChanged="@(async () => await ReconfigureAndReloadPivotTable())"> <DropDownListFieldSettings Value="ID" Text="Name"></DropDownListFieldSettings> </SfDropDownList> </div> <div> @if (pivotTableIsConfigured) { <SfPivotView TValue="ProductionBottleneckItem" Height="10000px" AllowGrouping="true" EnablePersistence="false" ShowToolbar="true" ShowTooltip="false" AllowDrillThrough="true" AllowConditionalFormatting="true" AllowDataCompression="true"> <PivotViewTemplates> <CellTemplate> @{ try { var templateContext = context as Syncfusion.Blazor.PivotView.AxisSet; var handled = false; if (templateContext != null && !string.IsNullOrEmpty(templateContext.FormattedText)) { switch (templateContext.Axis) { case "value": break; case "row": //step stuff if (templateContext.Level == 0) { if (selectedPivotType.StartsWith("By Step")) { var validStep = WorkflowSteps.ValidProductionSteps.FirstOrDefault(x => x.TextWithNumber == templateContext.FormattedText); if (validStep != null) { string style = $"background-color:{validStep.ColorName};"; <span style=@style> @templateContext.FormattedText <span style=@(style + "font-size:7px")>@validStep.ResponsiblePerson</span> </span> handled = true; } } } break; case "column": break; } if (!handled) { <span>@(templateContext.FormattedText ?? "")</span> } } } catch (Exception ex) { <span>Error</span> } } </CellTemplate> </PivotViewTemplates> <PivotViewDataSourceSettings DataSource="@bottleneckItems" ShowSubTotals="true" ShowHeaderWhenEmpty=false> <PivotViewColumns> @foreach (var pivotTableColumnName in pivotTableColumnNames) { <PivotViewColumn Name="@pivotTableColumnName" ShowNoDataItems="false"></PivotViewColumn> } </PivotViewColumns> <PivotViewRows> @foreach (var pivotTableRowName in pivotTableRowNames) { <div><PivotViewRow Name="@pivotTableRowName"></PivotViewRow></div> } </PivotViewRows> <PivotViewValues> @foreach (var pivotTableValueName in pivotTableValueNames) { <div><PivotViewValue Name="@pivotTableValueName"></PivotViewValue></div> } </PivotViewValues> <PivotViewCellEditSettings AllowAdding="false" AllowDeleting="false" AllowEditing="false" Mode=Syncfusion.Blazor.PivotView.EditMode.Normal></PivotViewCellEditSettings> <PivotViewConditionalFormatSettings> <PivotViewConditionalFormatSetting Measure="@nameof(ProductionBottleneckItem.RemainingHours)" Conditions=Condition.GreaterThan Value1=250> <PivotViewStyle BackgroundColor="firebrick" Color="white"/> </PivotViewConditionalFormatSetting> </PivotViewConditionalFormatSettings> <PivotViewFormatSettings> <PivotViewFormatSetting Name="@nameof(ProductionBottleneckItem.RemainingHours)" Format="#." UseGrouping="true"></PivotViewFormatSetting> <PivotViewFormatSetting Name="@nameof(ProductionBottleneckItem.CompletedHours)" Format="#." UseGrouping="true"></PivotViewFormatSetting> </PivotViewFormatSettings> </PivotViewDataSourceSettings> <SfSpinner @bind-Visible="@spinnerVisible"/> </SfPivotView> } else { <SfSpinner @bind-Visible="@spinnerVisible"/> } </div> @(bottleneckItems.Count()) items @code { SfDropDownList<IdNameDto, IdNameDto> locationDropDown; IdNameDto selectedLocation; SfDropDownList<string, string> pivotDropDown; string selectedPivotType; SfDropDownList<string, string> customerDropDown; string selectedCustomer; bool spinnerVisible = true; ObservableCollection<ProductionBottleneckItem> bottleneckItems { get; set; } = new ObservableCollection<ProductionBottleneckItem>(); protected override void OnInitialized() { selectedLocation = Services.EMService.LocationsCacheWithAny.First(); selectedPivotType = "By Step, PM, Project"; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { ConfigurePivotTable(); //don't refresh here - it happens automagically. } base.OnAfterRender(firstRender); } protected async Task ReconfigureAndReloadPivotTable() { if (pivotTableIsConfigured) { //keeps it from running when initial values are set in OnAfterRender await ConfigurePivotTable(); await RefreshPivotTableData(); } } protected List<string> pivotTableRowNames = new List<string>(), pivotTableColumnNames = new List<string>(), pivotTableValueNames = new List<string>(); protected bool pivotTableIsConfigured = false; private async Task ConfigurePivotTable() { pivotTableRowNames.Clear(); var splitPivotTypes = selectedPivotType.Split(','); foreach (var pivot in splitPivotTypes) { var pivotText = pivot.Trim(); if (pivotText.StartsWith("By ")) { pivotText = pivotText.Remove(0, 3); } switch (pivotText) { case "Step": pivotTableRowNames.Add(nameof(ProductionBottleneckItem.StepNameWithNumber)); break; case "PM": pivotTableRowNames.Add(nameof(ProductionBottleneckItem.ProjectManager)); break; case "Project": pivotTableRowNames.Add(nameof(ProductionBottleneckItem.ProjectNumber)); break; case "Labor": pivotTableRowNames.Add(nameof(ProductionBottleneckItem.LaborType)); break; } } pivotTableColumnNames.Clear(); pivotTableColumnNames.Add(nameof(ProductionBottleneckItem.MonthName)); pivotTableColumnNames.Add(nameof(ProductionBottleneckItem.Week)); pivotTableValueNames.Clear(); pivotTableValueNames.Add(nameof(ProductionBottleneckItem.RemainingHours)); //pivotTableValueNames.Add(nameof(ProductionBottleneckItem.CompletedHours)); pivotTableIsConfigured = true; } protected bool inRefreshPivotTableData = false; protected async Task RefreshPivotTableData() { if (inRefreshPivotTableData) { return; } inRefreshPivotTableData = true; spinnerVisible = true; try { bottleneckItems.Clear(); var query = new ProductionBottleneckQuery() { LocationId = selectedLocation?.Id ?? -1, Grouping = selectedPivotType ?? "", IsecFilter = (selectedCustomer == "ISEC"), HyVeeFilter = (selectedCustomer == "Hy-Vee"), JeffFilter = false, ProductionMeetingFilter = false, MaterialsOrderedFilter = false, DisplayMonths = 6, MonthsBackForBefore = 0, UserIdFilter = 0, }; var queryResults = await ClientHttp.Post<List<ProductionBottleneckItem>>("api/ProductionBottleneck/PivotTable", query); queryResults.ForEach(x => bottleneckItems.Add(x)); } catch (Exception ex) { NotificationsService.ShowPopupError(ex); } spinnerVisible = false; inRefreshPivotTableData = false; } } |