Blazor通用输入组件优化:如何传入完整属性而非仅属性值
实现Blazor智能通用输入组件:仅传DTO属性自动处理逻辑与类型适配
要实现只传入DTO属性就能自动完成ID生成、标签显示、必填判断,以及根据类型切换输入组件的需求,核心是通过表达式参数接收属性的完整元数据,而非仅传递属性值。以下是具体实现方案:
一、改造为泛型智能输入组件
将原组件改为泛型组件,通过Expression<Func<TModel, TValue>>接收属性表达式,在组件内部解析属性的元数据(名称、自定义特性、类型),自动完成所有逻辑处理。
完整组件代码:
@using System.ComponentModel.DataAnnotations @using System.Linq.Expressions @typeparam TModel @typeparam TValue <div class="mb-3"> <div class="input-group"> <div class="form-floating flex-grow-1"> @* 根据属性类型自动选择输入组件 *@ @if (IsNumericType) { <InputNumber id="@PropertyName" @bind-Value="@CurrentValue" class="form-control" placeholder=" " /> } else if (typeof(TValue) == typeof(DateTime) || typeof(TValue) == typeof(DateTime?)) { <InputDate id="@PropertyName" @bind-Value="@CurrentValue" class="form-control" placeholder=" " /> } else if (typeof(TValue) == typeof(bool)) { <div class="form-check mt-2"> <InputCheckbox id="@PropertyName" @bind-Value="@CurrentValue" class="form-check-input" /> <label class="form-check-label" for="@PropertyName">@DisplayLabel</label> </div> } else { <InputText id="@PropertyName" @bind-Value="@CurrentValue" class="form-control" placeholder=" " /> } @* 非布尔类型显示浮动标签 *@ @if (typeof(TValue) != typeof(bool)) { <label for="@PropertyName">@(DisplayLabel + (IsRequired ? " *" : string.Empty))</label> } </div> </div> @* 内置验证消息 *@ <ValidationMessage For="@PropertyExpression" class="text-danger mt-1" /> </div> @code { [Parameter, EditorRequired] public TModel Model { get; set; } [Parameter, EditorRequired] public Expression<Func<TModel, TValue>> PropertyExpression { get; set; } [Parameter] public EventCallback<TValue> ValueChanged { get; set; } private TValue CurrentValue { get => PropertyExpression.Compile().Invoke(Model); set => ValueChanged.InvokeAsync(value); } private string PropertyName { get; set; } private string DisplayLabel { get; set; } private bool IsRequired { get; set; } private bool IsNumericType { get; set; } protected override void OnInitialized() { // 解析表达式获取属性元数据 if (PropertyExpression.Body is not MemberExpression memberExpr) throw new InvalidOperationException("传入的表达式必须指向一个属性"); var propertyInfo = memberExpr.Member as System.Reflection.PropertyInfo; if (propertyInfo == null) throw new InvalidOperationException("表达式目标不是有效的属性"); // 获取属性名称(作为输入框ID) PropertyName = propertyInfo.Name; // 生成显示标签:优先用[Display]特性,无则转驼峰为友好格式 var displayAttr = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true) .FirstOrDefault() as DisplayAttribute; DisplayLabel = displayAttr?.Name ?? SplitCamelCase(PropertyName); // 判断是否必填 IsRequired = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Any(); // 判断是否为数值类型(含可空数值) var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)) ?? typeof(TValue); IsNumericType = underlyingType.IsPrimitive && underlyingType != typeof(bool) || underlyingType == typeof(decimal); } // 驼峰命名转空格分隔的友好名称 private string SplitCamelCase(string input) { return System.Text.RegularExpressions.Regex.Replace(input, @"([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim(); } }
二、使用示例
现在使用组件只需传入DTO实例和属性表达式,无需手动传入ID、标签、必填标记:
@code { private UserDto _user = new UserDto(); public class UserDto { [Required] [Display(Name = "用户名")] public string UserName { get; set; } [Display(Name = "年龄")] public int? Age { get; set; } [Display(Name = "注册日期")] public DateTime RegisterDate { get; set; } public bool IsActive { get; set; } } } <!-- 调用组件 --> <GenericInput Model="@_user" PropertyExpression="@(x => x.UserName)" /> <GenericInput Model="@_user" PropertyExpression="@(x => x.Age)" /> <GenericInput Model="@_user" PropertyExpression="@(x => x.RegisterDate)" /> <GenericInput Model="@_user" PropertyExpression="@(x => x.IsActive)" />
三、核心逻辑说明
- 表达式解析:通过
MemberExpression提取属性的PropertyInfo,直接获取属性的所有元数据,替代原有的外部扩展方法。 - 自动标签生成:优先读取
[Display]特性的Name字段,无则自动将驼峰命名转换为空格分隔的友好名称。 - 类型适配:根据属性类型自动切换
InputText/InputNumber/InputDate/InputCheckbox,覆盖常见数据类型场景。 - 双向绑定:通过
CurrentValue封装属性的读写逻辑,配合ValueChanged实现标准的Blazor双向绑定。 - 内置验证:集成
ValidationMessage,自动支持DTO上的数据验证特性(如[Required]、[StringLength]等)。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

