Question Dynamic Tab with @key Problem

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
361
Programming Experience
10+
Hello friends,
I am working on a Blazor Server App, here is the sample data.

SQL:
SET IDENTITY_INSERT [dbo].[OrdersDetail] ON
INSERT INTO [dbo].[OrdersDetail] ([Id], [ProductCode], [ProductName], [Quantity], [CostRatio], [UnitCost], [TotalBuyPrice], [TotalSellPrice], [ShippingNumber], [Status], [TrackingNumber], [Description], [OrderId], [VendorId], [Currency], [BuyUnitPrice], [SellUnitPrice]) VALUES (24, N'000001', N'Shirt', 100, 12, 0, 0, 0, N'shipment1', N'Shipped', N'tracking1', N'description1', 16, 1, N'TL', 11.1, 13)
INSERT INTO [dbo].[OrdersDetail] ([Id], [ProductCode], [ProductName], [Quantity], [CostRatio], [UnitCost], [TotalBuyPrice], [TotalSellPrice], [ShippingNumber], [Status], [TrackingNumber], [Description], [OrderId], [VendorId], [Currency], [BuyUnitPrice], [SellUnitPrice]) VALUES (25, N'000002', N'Gol', 50, 12, 22.512, 1005.0000000000001, 1250, N'shipment2', N'At customs', N'tracking2', N'description2', 16, 1, N'TL', 20.1, 25)
INSERT INTO [dbo].[OrdersDetail] ([Id], [ProductCode], [ProductName], [Quantity], [CostRatio], [UnitCost], [TotalBuyPrice], [TotalSellPrice], [ShippingNumber], [Status], [TrackingNumber], [Description], [OrderId], [VendorId], [Currency], [BuyUnitPrice], [SellUnitPrice]) VALUES (26, N'000003', N'Excipal', 35, 6, 62.54, 2065, 2310, N'shipment3', N'Getting ready', N'tracking3', N'description3', 16, 2, N'TL', 59, 66)
SET IDENTITY_INSERT [dbo].[OrdersDetail] OFF

I want to filter data that OrderId = 16 and based on this filtered data I want to dynamically create Tabs based on VendorId. According to these sample data at hand, 2 tabs need to be created dynamically.

Here is how I razor page;
C#:
@if(orderDetails != null)
    {
        <div class="row my-4">
            <div class="col-md-12">
                <RadzenCard>
                    <h3 class="h5">
                        Order @Order.OrderId Details
                        <RadzenBadge BadgeStyle="BadgeStyle.Secondary" Text=@($"{String.Format(new System.Globalization.CultureInfo("en-US"), "{0:C}", orderDetails?.Select(x => x.TotalBuyPrice).Sum())}") Class="float-right" />
                    </h3>
                    <RadzenTabs>
                        <Tabs>
                            @foreach (var detail in orderDetails)
                            {
                                <RadzenTabsItem @key="@detail.VendorId" Text="@detail.Vendor.Name">
                                    <RadzenDataGrid AllowFiltering="true" AllowPaging="true" AllowSorting="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
                                            Data="@orderDetails" TItem="OrderDetail" Class="mt-3">
                                        <Columns>
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="Id" Title="Number" Width="100px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="ProductCode" Title="Code" Width="130px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="ProductName" Title="Name" Width="130px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="Currency" Title="Currency" Width="95px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="Quantity" Title="Quantity" Width="95px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="BuyUnitPrice" Title="Unit Price" Width="95px" />
                                            <RadzenDataGridColumn TItem="OrderDetail" Property="TotalBuyPrice" Title="Total Price" Width="95px" />

                                        </Columns>
                                    </RadzenDataGrid>
                                </RadzenTabsItem>
                            }
                        </Tabs>
                    </RadzenTabs>
                </RadzenCard>
            </div>
        </div>
    }

What I expect is to have 2 tabs created dynamically. In the first tab, on the data grid, there will be VendorId = 1 data (2 records). In the second tab, on the data grid, there will be VendorId = 2 data (1 record).

I am getting this error:
C#:
Error: System.InvalidOperationException: More than one sibling of component 'Radzen.Blazor.RadzenTabsItem' has the same key value, '1'. Key values must be unique.

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ThrowExceptionForDuplicateKey(Object key, RenderTreeFrame& frame)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.BuildKeyToInfoLookup(DiffContext diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)

   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)

   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)

   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

VendorId is keyed, and Blazor is supposed to ignore rerendering the second VendorId = 1 which hasn't changed. I don't understand why the error occurred. @key
 
The problem is that you are expecting a UI component to figure out that you have two rows (OrderDetail.ID == 24 and OrderDetail.ID == 25) should be grouped together. The foreach loop on line 12 will loop 3 times, not 2 times as you are expecting.
 
this solved my problem
C#:
@if(orderDetails != null)
    {
        <div class="row my-4">
            <div class="col-md-12">
                <RadzenCard>
                    <h3 class="h5">
                        Order @Order.OrderId Details
                        <RadzenBadge BadgeStyle="BadgeStyle.Secondary" Text=@($"{String.Format(new System.Globalization.CultureInfo("en-US"), "{0:C}", orderDetails?.Select(x => x.TotalBuyPrice).Sum())}") Class="float-right" />
                    </h3>
                    <RadzenTabs>
                        <Tabs>
                            @{var detailVendorId = 0;}
                            @foreach (var detail in orderDetails)
                            {
                                @if(detailVendorId != detail.VendorId){
                                    <RadzenTabsItem Text="@detail.Vendor.Name">
                                        <RadzenDataGrid AllowFiltering="true" AllowPaging="true" AllowSorting="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
                                                        Data="@orderDetails.Where(x=>x.VendorId == detail.VendorId)" TItem="OrderDetail" Class="mt-3">
                                            <Columns>
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="Id" Title="Number" Width="100px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="ProductCode" Title="Code" Width="130px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="ProductName" Title="Name" Width="130px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="Currency" Title="Currency" Width="95px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="Quantity" Title="Quantity" Width="95px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="BuyUnitPrice" Title="Unit Price" Width="95px" />
                                                <RadzenDataGridColumn TItem="OrderDetail" Property="TotalBuyPrice" Title="Total Price" Width="95px" />

                                            </Columns>
                                        </RadzenDataGrid>
                                    </RadzenTabsItem>
                                }
                                detailVendorId = detail.VendorId;
                            }
                        </Tabs>
                    </RadzenTabs>
                </RadzenCard>
            </div>
        </div>
    }
 
Back
Top Bottom