Repository: dotnetcore/BootstrapBlazor (2025-07-01T03:56:52Z)

Issue:
bug(LookupFilter): use first item as default value when value is null
### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

use first item as default value when value is null

### Expected Behavior

use first item as default value when value is null

### Interactive render mode

Interactive Server (Interactive server-side rendering (interactive SSR) using Blazor Server)

### Steps To Reproduce

none

### Exceptions (if any)

_No response_

### .NET Version

NET9.0

### Anything else?

_No response_


Discussion:
@ArgoZhang Thank you for filing this issue. In order for us to investigate this issue, please provide [a minimalistic repro project](https://github.com/dotnetcore/bootstrapblazor/blob/main/docs/repro.md) that illustrates the problem.



Patch:
diff --git a/src/BootstrapBlazor/Components/Filters/FilterBase.cs b/src/BootstrapBlazor/Components/Filters/FilterBase.cs
index 4b8ad3bf8e1..0a9ee142801 100644
--- a/src/BootstrapBlazor/Components/Filters/FilterBase.cs
+++ b/src/BootstrapBlazor/Components/Filters/FilterBase.cs
@@ -38,11 +38,6 @@ public abstract class FilterBase : BootstrapModuleComponentBase, IFilterAction
     [Parameter]
     public int Count { get; set; }
 
-    /// <summary>
-    /// 获得/设置 多个条件逻辑关系符号
-    /// </summary>
-    protected FilterLogic Logic { get; set; }
-
     /// <summary>
     /// 获得/设置 所属 TableFilter 实例
     /// </summary>
@@ -55,6 +50,11 @@ public abstract class FilterBase : BootstrapModuleComponentBase, IFilterAction
     [CascadingParameter]
     protected FilterContext? FilterContext { get; set; }
 
+    /// <summary>
+    /// 获得/设置 多个条件逻辑关系符号
+    /// </summary>
+    protected FilterLogic Logic { get; set; }
+
     /// <summary>
     /// <inheritdoc/>
     /// </summary>
diff --git a/src/BootstrapBlazor/Components/Filters/LookupFilter.razor.cs b/src/BootstrapBlazor/Components/Filters/LookupFilter.razor.cs
index 28a1228fc16..01160714d76 100644
--- a/src/BootstrapBlazor/Components/Filters/LookupFilter.razor.cs
+++ b/src/BootstrapBlazor/Components/Filters/LookupFilter.razor.cs
@@ -18,9 +18,9 @@ public partial class LookupFilter
     /// <summary>
     /// <inheritdoc/>
     /// </summary>
-    protected override void OnParametersSet()
+    protected override async Task OnParametersSetAsync()
     {
-        base.OnParametersSet();
+        await base.OnParametersSetAsync();
 
         if (TableColumnFilter != null)
         {
@@ -28,6 +28,19 @@ protected override void OnParametersSet()
             _isShowSearch = column.ShowSearchWhenSelect;
             _type = column.PropertyType;
             _lookup = column;
+
+            if (string.IsNullOrEmpty(_value))
+            {
+                var service = _lookup.LookupService;
+                if (service != null)
+                {
+                    var items = await _lookup.GetItemsAsync(service, _lookup.LookupServiceKey, _lookup.LookupServiceData);
+                    if(items != null)
+                    {
+                        _value = items.FirstOrDefault()?.Value;
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/BootstrapBlazor/Components/Select/Select.razor.cs b/src/BootstrapBlazor/Components/Select/Select.razor.cs
index 5f04d51841c..51f01b4aadb 100644
--- a/src/BootstrapBlazor/Components/Select/Select.razor.cs
+++ b/src/BootstrapBlazor/Components/Select/Select.razor.cs
@@ -27,7 +27,8 @@ public partial class Select<TValue> : ISelect, ILookup
     private ILookupService? InjectLookupService { get; set; }
 
     /// <summary>
-    /// Gets or sets a value indicating whether the "active" state should be used when the associated value is null.
+    /// 获得/设置 值为 null 时是否使用第一个选项作为默认值
+    /// <para>Gets or sets a value indicating whether the "active" state should be used when the associated value is null.</para>
     /// </summary>
     [Parameter]
     public bool IsUseActiveWhenValueIsNull { get; set; }


Test patch:
diff --git a/test/UnitTest/Components/TableLookupFilterTest.cs b/test/UnitTest/Components/TableLookupFilterTest.cs
index b5b19c04b19..39a1a40c441 100644
--- a/test/UnitTest/Components/TableLookupFilterTest.cs
+++ b/test/UnitTest/Components/TableLookupFilterTest.cs
@@ -31,17 +31,18 @@ public async Task FilterAction_Ok()
         });
         var lookup = cut.FindComponent<LookupFilter>();
         var filter = lookup.Instance;
-
         var newConditions = new FilterKeyValueAction()
         {
             Filters =
             [
-                new FilterKeyValueAction() { FieldValue = "1" },
+                new FilterKeyValueAction() { FieldValue = "2" },
             ]
         };
         await cut.InvokeAsync(() => filter.SetFilterConditionsAsync(newConditions));
         var conditions = filter.GetFilterConditions();
         Assert.Single(conditions.Filters);
+        Assert.Equal("2", conditions.Filters[0].FieldValue);
+
         await cut.InvokeAsync(() => filter.Reset());
         conditions = filter.GetFilterConditions();
         Assert.Empty(conditions.Filters);
@@ -64,6 +65,51 @@ public async Task FilterAction_Ok()
         Assert.Empty(conditions.Filters);
     }
 
+    [Fact]
+    public async Task LookupService_Ok()
+    {
+        var cut = Context.RenderComponent<TableColumnFilter>(pb =>
+        {
+            pb.Add(a => a.Table, new MockTable());
+            pb.Add(a => a.Column, new MockLookupServiceColumn());
+        });
+        var lookup = cut.FindComponent<LookupFilter>();
+        var filter = lookup.Instance;
+
+        // 由于 LookupFilter 默认值未设置使用候选项第一个
+        cut.WaitForAssertion(() => cut.Contains("value=\"LookupService-Test-1-async\""), TimeSpan.FromMilliseconds(100));
+        var newConditions = new FilterKeyValueAction()
+        {
+            Filters =
+            [
+                new FilterKeyValueAction() { FieldValue = "v2" },
+            ]
+        };
+        await cut.InvokeAsync(() => filter.SetFilterConditionsAsync(newConditions));
+        var conditions = filter.GetFilterConditions();
+        Assert.Single(conditions.Filters);
+        Assert.Equal("v2", conditions.Filters[0].FieldValue);
+
+        await cut.InvokeAsync(() => filter.Reset());
+        conditions = filter.GetFilterConditions();
+        Assert.Empty(conditions.Filters);
+    }
+
+    [Fact]
+    public async Task LookupService_Empty()
+    {
+        var column = new MockEmptyLookupServiceColumn();
+        var cut = Context.RenderComponent<TableColumnFilter>(pb =>
+        {
+            pb.Add(a => a.Table, new MockTable());
+            pb.Add(a => a.Column, column);
+        });
+        var lookup = cut.FindComponent<LookupFilter>();
+        var filter = lookup.Instance;
+
+        await column.Task;
+    }
+
     class MockTable : ITable
     {
         public Dictionary<string, IFilterAction> Filters { get; set; } = [];
@@ -89,4 +135,60 @@ public MockColumn()
             };
         }
     }
+
+    class MockLookupServiceColumn : TableColumn<Foo, string>
+    {
+        public MockLookupServiceColumn()
+        {
+            PropertyType = typeof(string);
+            FieldName = "Lookup";
+            LookupService = new LookupFilterService();
+            LookupServiceKey = "LookupKey";
+        }
+    }
+
+    class MockEmptyLookupServiceColumn : TableColumn<Foo, string>
+    {
+        private LookupFilterService _service = new LookupFilterService();
+
+        public MockEmptyLookupServiceColumn()
+        {
+            PropertyType = typeof(string);
+            FieldName = "Lookup";
+            LookupService = _service;
+            LookupServiceKey = "LookupEmptyKey";
+        }
+
+        public Task Task => _service.Task;
+    }
+
+    class LookupFilterService : LookupServiceBase
+    {
+        private TaskCompletionSource _taskCompletionSource = new();
+
+        public override IEnumerable<SelectedItem>? GetItemsByKey(string? key, object? data) => null;
+
+        public override async Task<IEnumerable<SelectedItem>?> GetItemsByKeyAsync(string? key, object? data)
+        {
+            IEnumerable<SelectedItem>? ret = null;
+
+            if (key == "LookupKey")
+            {
+                await Task.Delay(30);
+                ret =
+                [
+                    new SelectedItem("v1", "LookupService-Test-1-async"),
+                    new SelectedItem("v2", "LookupService-Test-2-async")
+                ];
+            }
+            else if (key == "LookupEmptyKey")
+            {
+                ret = [];
+                _taskCompletionSource.TrySetResult();
+            }
+            return ret;
+        }
+
+        public Task Task => _taskCompletionSource.Task;
+    }
 }