Pivot Table Performance Blazor Client

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;

    }


}





1 Reply

MM Manikandan Murugesan Syncfusion Team February 23, 2022 09:02 AM UTC

Hi Jonah, 
 
When compared to the Blazor Server side app, the Blazor WASM app has performance issues. Because it downloads the C# libraries first and then processes them in browsers. The pivot table component also experiences some delays when displaying a larger number of cells. Use a remote URL to bind JSON and CSV data and set the "ExpandAll" property to "false" to improve performance. Please see the document below for more information. 
 
 
To avoid this limitation in the Blazor WASM app, we intend to provide support for using the existing server-side engine (EJ2.Pivot) in the Blazor Pivot Table. And it will be available in any of our upcoming releases. You can follow the progress by clicking on the link below. 
 
 
However, we tested the blazor server application with the “CellTemplate” option, and it works flawlessly with the data source in our sample browser. Please follow the link below to find it. 
 
 
If the problem persists in blazor server application, please reproduce it in the provided sample and revert to us (or) send your sample that replicates the problem. This would allow us to investigate the reported problem at our end and provide a solution as soon as possible. 
 
Regards, 
Manikandan 


Loader.
Up arrow icon