radzenhq / radzen-blazor

Radzen Blazor is a set of 90+ free native Blazor UI components packed with DataGrid, Scheduler, Charts and robust theming including Material design and FluentUI.
https://www.radzen.com
MIT License
3.52k stars 786 forks source link

RadzenDropdown with multiple selection, chips and remote data /virtualization cannot show selections on load #1692

Closed vadimb7 closed 4 weeks ago

vadimb7 commented 4 weeks ago

Describe the bug Environment: BlazorWASM, with RadzenDropdown, multiple selections, virtualized data using LoadData delegate. int ID for Value Property + string For Text Property. Pre-selected items which are not loaded on the first page of the virtualized control.

  1. Items/chips do not show up until dropdown is scrolled far enough for control to obtain text labels from the list

To Reproduce

  1. Setup control similar to:
    
    <RadzenDropDown Multiple="true" AllowClear="true" AllowFiltering="true" Chips="true" Data=@_ItemList 
                @bind-Value=@_selectedItems AllowVirtualization="true" LoadData=@LoadMyData
                FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" 
                FilterOperator="StringFilterOperator.Contains" FilterPlaceholder="Test Items here"
                TextProperty="@nameof(Test.TestLabel)" ValueProperty="@nameof(Test.TestID)"
                Style="width:100%;max-width:300px"
                ClearSearchAfterSelection="false" />
2. Implement LoadMyData method to retrieve remote data with proper paging/filtering/sorting
3. Selected Items:

IList _selectedItems = new List();

and further on while initializing

protected override async Task OnInitializedAsync() { ... _selectedItems.Add(82); _selectedItems.Add(137); .... }



**Expected behavior:** 
Control makes calls to retrieve labels (TextProperty) for selected items, even if they're not in the virtualized view

**Actual behavior:** 
Chips do not show up until the dropdown is scrolled to the point where selected items are on the list, there seem to be no provision made for this scenario.

**Desktop:**
 - OS: Windows 11
 - Browser: any
 - Version: 5.1.11
enchev commented 4 weeks ago

Nothing will notify the UI when you add items to a simple IList.

vadimb7 commented 4 weeks ago

Not sure what you are referring to. The Items are pre-selected to start with. Control gets notification via parameters, as usual. If the entire list is loaded (and not paged/virtualized) the problem does not manifest.

vadimb7 commented 3 weeks ago

Please reconsider - the following code below (blazor WASM) reproduces errant behavior.

Note how only one selected item shows up initially, scroll, then select some items from a different range - say mid-5000 IDs. At some point selection will be cleared completely and it'd start anew.

@using LinqToDB
@using LinqToDB.Extensions
@using System.Linq.Expressions

<PageTitle>Test Dropdown</PageTitle>

<h1>Test</h1>

<RadzenDropDown Multiple="true" AllowClear="true" AllowFiltering="true" Chips="true" Data=@_itemList
                @bind-Value=@_selectedItems AllowVirtualization="true" LoadData="@LoadData"
                FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" Count="@_itemCount"
                FilterOperator="StringFilterOperator.Contains" FilterPlaceholder="Account Name here"
                TextProperty="@nameof(TestItem.Name)" ValueProperty="@nameof(TestItem.ID)"
                Style="width:100%;"
                ClearSearchAfterSelection="false" VirtualizationOverscanCount="10" />

<p>Selected items:</p>
@foreach (var item in _selectedItems)
{
    <p>Selected Item: @item</p>
}

@code {
    class TestItem
    {
        public int ID { get; set; }
        public string Name { get; set; }

    }

    IEnumerable<TestItem> _itemList;
    IList<int> _selectedItems = new List<int>();
    int _itemCount;
    string? _lastFilter;

    List<TestItem> _baseItemList = new List<TestItem>();

    protected override async Task OnInitializedAsync()
    {
        // var dc = new IronCalendarDataContext();
        // await dc.InitAsync();
        var rnd = new Random();
        const string srcChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for (int i = 1; i < 10000; ++i)
        {
            string nm = new string(Enumerable.Repeat(srcChars, 8).Select(s => s[rnd.Next(s.Length)]).ToArray());

            _baseItemList.Add(new TestItem() { ID = i, Name = $"{nm} - {i}" });
        }

        // _baseItemList = _baseItemList.OrderBy(t => t.Name, StringComparer.InvariantCultureIgnoreCase).ToList();
        _selectedItems.Add(7);
        _selectedItems.Add(925);
        _selectedItems.Add(3075);

        await base.OnInitializedAsync();
    }

    async Task LoadData(LoadDataArgs args)
    {
        int qcount = 0;

        if (String.IsNullOrWhiteSpace(args.Filter) ^ String.IsNullOrWhiteSpace(_lastFilter))
        {
            args.Skip = 0;
        }
        else if (!string.IsNullOrWhiteSpace(args.Filter) && !String.IsNullOrWhiteSpace(_lastFilter) && String.Compare(_lastFilter, args.Filter, true) != 0)
        {
            args.Skip = 0;
            args.Top = null;
        }

        _lastFilter = args.Filter;

        Expression<Func<TestItem, bool>> e = a => String.IsNullOrWhiteSpace(args.Filter) || 
                                             a.Name!.StartsWith(args.Filter, StringComparison.InvariantCultureIgnoreCase);
        var ce = e.Compile();

        Console.WriteLine($"About to get items {args.Skip} : {args.Top} : {args.Filter}");

        var q = _baseItemList
            .Where(ce)
            .Skip(args.Skip.HasValue ? args.Skip.Value : 0)
            .Take(args.Top.HasValue ? args.Top.Value : Int32.MaxValue).ToList();
        if (String.IsNullOrWhiteSpace(args.Filter))
        {
            qcount = _baseItemList.Count();
        }
        else
        {
            // cache based on lastfilter
            qcount = _baseItemList.Where(ce).Count();
        }

        _itemList = await Task.FromResult(q);
        _itemCount = qcount;
    }

}