☰
ant-design-blazor 中 Select 选择器的基本使用:从 DataSource 到双向绑定完整指南
2026/10/12 3:27:32 网站建设 项目流程
  • 前端
  • UI组件
  • 设计系统

【免费下载链接】ant-design-blazor

基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。

项目地址:https://gitcode.com/ant-design-blazor/ant-design-blazor
点击查看免费下载

导读

本文基于 ant-design-blazor 官方示例中Select组件的「基本使用(Basic Usage)」演示,系统讲解 Blazor 场景下选择器最核心的三种用法:基于DataSource数据源渲染选项、手动声明SelectOption子选项,以及通过@bind-Value实现双向绑定。读者读完可掌握ItemValue/ItemLabel/ValueName/LabelName等数据字段映射参数的区别与取舍,理解Disabled、Loading、AllowClear、Placeholder、OnSelectedItemChanged等高频配置项的用法,并知晓选项如何从数据源同步到内部SelectOptionItem的底层机制。

演示示例概览

官方演示位于 site/AntDesign.Docs/Demos/Components/Select/demo/Basic.razor,其元数据文档 basic.md 声明该示例主题为「基本使用」。示例一次性展示了 7 个 Select 实例,覆盖了从「最简单的字符串选项列表」到「复杂对象 + 字典数据源」的渐进式用法:

实例数据形态核心演示点
1List<Person>对象数据源DataSource+ItemValue/ItemLabel+DisabledName
2手写SelectOptionDisabled选项、DefaultValue、整体禁用
3手写SelectOption+ 子内容Loading加载态、选项自定义文本
4List<Person>+AllowClearValueName/LabelName字符串映射、Placeholder
5List<string>OnSelectedItemChanged事件回调
6Dictionary<string,string>ItemLabel/ItemValue作用于键值对、DisabledPredicate谓词
7List<Person>+SelectOptionsSelectOptions模板批量生成选项

所有实例共用同一份@code数据:_list(4 个Person对象,其中disabled条目被标记为禁用)、_personNames(纯字符串列表)、_dict(由_list转换而来的字典)。

基于 DataSource 渲染选项:字段映射三件套

第一种用法面向最常见的「集合驱动」场景,将List<Person>直接绑定为选项来源:

<Select DataSource="@_list" @bind-Value="@_selectedValue1" DefaultValue="@("lucy")" ItemValue="c=>c.Value" ItemLabel="c=>c.Name" DisabledName="@nameof(Person.IsDisabled)" Style="width:120px"> </Select>

这里涉及四个关键参数,分别解决「选项值是什么」「选项文字是什么」「哪个选项不可选」三个问题:

  • ItemValue(Func<TItem, TItemValue>):从数据项中提取选项值(Value)的委托,示例中取c.Value;
  • ItemLabel(Func<TItem, string>):从数据项中提取选项显示文字(Label)的委托,示例中取c.Name;
  • DisabledName(string):以属性名字符串指定禁用标志字段,示例中指向Person.IsDisabled;
  • DefaultValue(TItemValue):组件初始化以及表单执行 Reset 时使用的默认值,示例中为"lucy"。

从源码看,ItemValue/ItemLabel本质上是_getValue/_getLabel两个委托的公开入口(SelectBase.razor.cs),而DisabledName在set访问器中通过PathHelper.GetDelegate<TItem, bool>(value)将属性名字符串编译为读取委托(Select.razor.cs)。也就是说,DisabledName与DisabledPredicate最终都落到同一个_getDisabled委托上,只是入口形式不同——前者是字符串属性名,后者是直接传入的Func<TItem, bool>。

ItemValue/ItemLabel 与 ValueName/LabelName 的选择

示例中第 4 个实例给出了字段映射的另一种写法——字符串属性名:

<Select DataSource="@_list" @bind-Value="@_selectedValue4" ValueName="@nameof(Person.Value)" LabelName="@nameof(Person.Name)" DisabledName="@nameof(Person.IsDisabled)" Style="width: 120px;" Placeholder="Choose" AllowClear> </Select>

ValueName与LabelName同样通过PathHelper.GetDelegate在 set 访问器中生成委托(SelectBase.razor.cs)。两种写法的功能等价,区别在于:

  • ItemValue/ItemLabel是强类型委托,编译器可直接校验类型,重构时随属性改名而更新;
  • ValueName/LabelName是字符串,运行期解析,适合属性名来自配置或外部数据的场景,但拼写错误要到运行期才会暴露。

组件类注释明确提示:使用ItemValue时不应同时使用ValueName,使用ItemLabel时不应同时使用LabelName(见 Select.razor.cs 中被标记为[Obsolete]的旧入口即可知晓演进脉络)。需要特别注意的是初始化校验:当DataSource非空、TItemValue与TItem不是同一类型,且既未提供ValueProperty也未提供ValueName时,OnInitialized会直接抛出ArgumentNullException(nameof(ValueName))(Select.razor.cs)。因此对象数据源必须显式声明值字段映射。

DisabledPredicate 与字典数据源

第 6 个实例演示了函数式禁用判断与字典数据源的组合:

<Select DataSource="@_dict" @bind-Value="@_selectedValue6" ItemLabel="c=>c.Key" ItemValue="c=>c.Value" DisabledPredicate="@(c=>c.Key == "Disabled")" Style="width: 120px;" Placeholder="Dictionary options"> </Select>

这里DataSource是Dictionary<string, string>,通过ItemLabel="c=>c.Key"、ItemValue="c=>c.Value"把字典键作为显示文字、字典值作为绑定值。DisabledPredicate接收一个Func<TItem, bool>谓词,示例中凡 Key 为"Disabled"的条目都会被标记为不可选。它比DisabledName更灵活——不要求数据项有独立布尔字段,任何可计算条件都可用。

手写 SelectOption:小型固定选项集的最简方式

当选项数量少且固定时,可以直接在组件体内声明SelectOption,完全不需要数据源。示例第 2 个实例同时演示了「带禁用项」与「整体禁用」两种状态:

<Select @bind-Value="@_selectedValue2" DefaultValue="@("lucy")" Style="width: 120px;" TItemValue="string" TItem="string" Disabled> <SelectOption Value="@("jack")" Label="Jack" /> <SelectOption Value="@("lucy")" Label="Lucy" /> <SelectOption Value="@("disabled")" Label="Disabled" Disabled /> <SelectOption Value="@("yaoming")" Label="Yaoming" /> </Select>

SelectOption的Value与Label参数分别决定选中值与显示文字,Disabled参数使单个选项不可选。当TItemValue/TItem同为string时,代码中可以省略类型参数声明,由编译器推断。组件级Disabled参数(定义于 SelectBase.razor.cs)则整体禁用整个选择器,此时选项仍可见但不可交互。

第 3 个实例展示了另一种选项形态——通过子内容自定义选项文字,并配合Loading显示加载态:

<Select @bind-Value="@_selectedValue3" DefaultValue="@("lucy")" Style="width: 120px;" TItemValue="string" TItem="string" Loading> <SelectOption Value="@("jack")">Jack</SelectOption> <SelectOption Value="@("lucy")">Lucy</SelectOption> <SelectOption Value="@("disabled")" Disabled>Disabled</SelectOption> <SelectOption Value="@("yaoming")">Yaoming</SelectOption> </Select>

从渲染层看,SelectOption的ChildContent优先于ItemTemplate,其次才是InternalLabel文本(SelectOption.razor)。因此「<SelectOption Value="...">自定义内容</SelectOption>」可以渲染任意富内容,而Label参数则作为选中后回显文字、无障碍aria-label与搜索匹配的依据。

手写选项的底层注册机制

手写SelectOption时,组件内部并不会凭空出现选项。从 SelectOption.razor.cs 可以看清注册链路:OnInitializedAsync中若父级SelectParent.HasSelectOptions为真,SelectOption会创建一个新的SelectOptionItem<TItemValue, TItem>(包含InternalId、Label、Value、IsDisabled等),再通过SelectParent.AddOptionItem交给父组件维护在SelectOptionItems集合中(Select.razor.cs)。而组件树的CascadingValue结构(SelectBase.razor)保证了选项与父级 Select 的关联,这也解释了为什么手写选项必须放在<Select>...</Select>的ChildContent之内。

SelectOptions 模板:批量生成选项的折中方案

第 7 个实例展示了第三种选项来源:SelectOptions渲染模板配合@foreach循环批量生成SelectOption:

<Select TItem="string" TItemValue="string" @bind-Value="_selectedValue7" Style="width:120px;" Placeholder="Select option content"> <SelectOptions> @foreach (var item in _list) { <SelectOption TItemValue="string" TItem="string" Value="@item.Value"> <span>@item.Name (@item.Value)</span> </SelectOption> } </SelectOptions> </Select>

SelectOptions是Select组件上类型为RenderFragment的参数(Select.razor.cs),ChildContent是它的别名(OnInitialized中会将ChildContent赋值给SelectOptions)。该方案的价值在于:既能像DataSource那样用循环动态生成选项,又能像手写SelectOption那样逐项定制渲染内容——示例中每项显示为名称 (值)的复合文本。渲染时下拉列表会对SelectOptions中的SelectOption组件逐项挂载(Select.razor)。

双向绑定与事件回调:数据流闭环

@bind-Value是 Select 与外界数据交互的主通道,其背后是Value参数与ValueChanged回调的组合(Select.razor.cs)。当用户在下拉中选择某一项时,内部OnValueChangeAsync会查找匹配的SelectOptionItem,将其标记为选中并回调ValueChanged(Select.razor.cs);当外部代码修改绑定字段时,EvaluateValueChangedOutsideComponent负责把旧的选中项反选、新的选中项加入SelectedOptionItems(Select.razor.cs),实现内外双向同步。

除了@bind-Value,示例还演示了OnSelectedItemChanged——它以数据项本身(TItem)而非值(TItemValue)为参数的事件回调:

<Select TItem="string" TItemValue="string" DataSource="@_personNames" @bind-Value="@_selectedValue5" Style="width: 120px;" Placeholder="Choose" OnSelectedItemChanged="@((personName) => Console.WriteLine($"selectedItem:{personName},selectedValue:{_selectedValue5}"))"> </Select>

此例的DataSource是List<string>,即TItem与TItemValue同为string,数据项本身就可直接当作值使用,无需字段映射。OnSelectedItemChanged定义于 SelectBase.razor.cs,当选中项变化时触发,适合需要拿到完整数据对象做后续逻辑(如联动查询)的场景。示例代码块注释中还保留了一个OnSelectedItemChangedHandler(Person value)方法,展示对象数据源下拿到Person实例后如何消费其属性。

高频展示参数:Placeholder、AllowClear、Loading 与默认值

贯穿多个实例的还有一组「观感类」参数,值得逐一说明其语义:

  • Placeholder:未选中任何选项时显示在输入框中的提示文字(SelectBase.razor.cs),示例中多次使用"Choose"、"Dictionary options"、"Select option content"作为占位提示;
  • AllowClear:显示清除按钮。点击清除按钮后内部走OnInputClearClickAsync链路(SelectBase.razor.cs),将选中项清空并把default(TItemValue)通过双向绑定回写,最后还会触发OnClearSelected事件。需注意源码注释提示:若Value类型的 default 值恰好也是某个选项的值,清除按钮可能不生效,除非配合ValueOnClear;
  • Loading:展示加载中状态。组件注释明确说明它只是一个视觉开关,「加载逻辑需要你自己实现」(SelectBase.razor.cs),通常配合异步数据获取时先Loading=true、数据到达后置 false;
  • Disabled:整体禁用组件(SelectBase.razor.cs);
  • DefaultValue:仅对Mode = default生效的初始值,同时在表单Reset按钮触发时会恢复为该值(Select.razor.cs)。从OnAfterRenderAsync的初始化逻辑看,DefaultValue只有在Value为 null 且存在匹配选项时才会被应用(Select.razor.cs);
  • Style:示例统一使用width: 120px(以及width: 100%的默认值),控制选择器宽度,实际项目中应结合布局需求设置。

底层原理:DataSource 变化检测与选项同步

理解DataSource驱动方式的关键,是组件如何感知集合变化并重建选项。在 Select.razor.cs 的EvaluateDataSourceChange中可以看到完整的差异检测逻辑:

  • 对原始类型(如string、int)数据源,直接比较前后序列是否SequenceEqual;
  • 对复杂对象数据源,组件会通过反射调用MemberwiseClone生成浅拷贝列表,再借助DataSourceEqualityComparer比较引用是否变化,从而判断是「数据项内容变了」还是「集合整体换了」;
  • 检测到变化后触发OnDataSourceChanged回调,并在OnParametersSetAsync中调用CreateDeleteSelectOptions增量重建SelectOptionItems(Select.razor.cs)。

增量重建遵循两个原则:IgnoreItemChanges(默认 true)为 true 时,集合中消失的选项会被移除;为 false 时则整体清空重建,以便让Label、禁用状态等字段变化生效。源码还特别处理了「已添加的自定义标签(AddedTags)」不被数据源重建误删,以及「被选中项在数据源中被移除时保留其值」的边界场景。

这一行为有直接的测试佐证:测试 Select.Value.Tests.razor 中的Keep_value_when_corresponding_item_in_DataSource_removed用例验证了——当选中项从DataSource移除后,Value保持不变、ValueChanged不会触发、选中显示清空,组件通过IEqualityComparer<TItem>实现对数据项身份的稳定追踪。

在表单中使用 Select

示例虽未直接展示表单,但从组件继承体系可以推断其表单集成能力:Select<TItemValue, TItem>继承自AntInputComponentBase<TItemValue>,天然支持表单验证与重置语义。DefaultValue的注释明确提到「用于初始化以及按下表单 Reset 按钮时」(Select.razor.cs);同时OnParametersSetAsync中当Value变化且Form.ValidateOnChange开启时会调用EditContext.NotifyFieldChanged触发字段验证(Select.razor.cs)。因此DefaultValue在多值模式(Mode = multiple | tags)下的对应参数是DefaultValues(SelectBase.razor.cs),两者分别服务于单选与多选两种场景。

小结

回到「基本使用」这个主题:ant-design-blazor 的 Select 组件通过三种选项来源(DataSource数据源、手写SelectOption、SelectOptions模板)覆盖了从静态小列表到动态大数据集的全部常见形态;通过ItemValue/ItemLabel(委托)与ValueName/LabelName(属性名)两套字段映射语法适配不同编程风格;通过@bind-Value与OnSelectedItemChanged构成完整的数据流闭环。在此基础上,DefaultValue、AllowClear、Loading、Placeholder、Disabled等参数进一步支撑了初始化、清空、异步加载等日常需求。若需进一步深入,可继续查阅 Select.razor.cs 与 SelectBase.razor.cs 的完整实现,以及 tests/AntDesign.Tests/Select 目录下针对DataSource、SelectOptions、Tags、Values等行为的专项测试。

  • 前端
  • UI组件
  • 设计系统

【免费下载链接】ant-design-blazor

基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。

项目地址:https://gitcode.com/ant-design-blazor/ant-design-blazor
点击查看免费下载

相关推荐

上一篇:终极AMD Ryzen调试工具SMUDebugTool:从新手到专家的完整硬件掌控指南
下一篇:大众点评数据采集终极指南:破解动态字体加密的完整解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询