如何在 Blazor 中覆盖 InputBase<T> 值,以进行验证
Posted
技术标签:
【中文标题】如何在 Blazor 中覆盖 InputBase<T> 值,以进行验证【英文标题】:How to override InputBase<T> Value in Blazor, in order to validate 【发布时间】:2020-12-21 09:45:40 【问题描述】:我正在尝试构建一个新的 Blazor 输入组件,它继承自 InputBase,以便我可以提供一个表单输入字段来选择组织中的员工。我需要提供将输入限制为单个人或允许多项选择的功能,具体取决于用例。
我正在构建的输入组件接受“员工”类型的列表,这是一个专门为我们的员工数据集构建的类。我已经设置了一个布尔值来表示是否应该允许多选。但是,如果应该将其限制为单个选择,我无法拦截 Value 的更改以阻止添加其他人。
我已经尝试过以下对 InputBase Value 属性的覆盖:
new public List<Employee> Value
get
return CurrentValue;
set
if (!Multiselect)
CurrentValue = value.Take(1).ToList();
else
CurrentValue = value;
由于有一个 List 绑定,我已经覆盖了 FormatValueAsString 和 TryParseValueFromString 以便可以在输入字段中看到电子邮件地址,作为组件的一部分。我可以将多选逻辑添加到这些函数中,但是它们仍然允许 Value 以多个 in 结尾,尽管 UI 中显示的字符串只会包含一个。
编辑: 根据要求添加更多代码。
.razor 文件:
@inherits InputBase<List<Employee>>
@using Project.Models.Employee
<div class="InputEmployee">
<input class="InputEmployee" @bind="CurrentValueAsString" type="text" />
<i class="oi oi-person" @onclick="() => openSearch()"></i>
</div>
<div class="InputEmployeeSearch @(displaySearch ? "": " collapse")" @onblur="() => toggleSearch()" id="searchInput">
<div class="row">
<div class="searchInput">
<input @bind-Value="searchString" @bind-Value:event="oninput" placeholder="Search by name, email address, employee number" type="text" />
</div>
</div>
@if (activeSearch)
<div class="row">
<div class="searchActive">
Searching...
</div>
</div>
@if (foundEmployees != null && foundEmployees.Any())
<div class="row">
<div class="searchResults">
@foreach (Employee employee in foundEmployees)
<div class="employeeResult" @onclick="() => select(employee)">
@employee.DisplayName
</div>
</div>
</div>
@if (errorMessage != null)
<div class="row">
<div class="searchError">
@errorMessage
</div>
</div>
</div>
.razor.cs 文件:
using Project.Models.Employee;
using Project.Services.Employee;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
namespace Project.Components
public partial class InputEmployee : InputBase<List<Employee>>
[Inject]
private IEmployeeDataService EmployeeDataService get; set;
[Parameter]
public int Debounce get; set; = 500;
[Parameter]
public int MaximumResults get; set; = 10;
[Parameter]
public int MinimumLength get; set; = 4;
[Parameter]
public bool Multiselect get; set; = false;
new public List<Employee> Value
get
return CurrentValue;
set
if (!Multiselect)
CurrentValue = value.Take(1).ToList();
else
CurrentValue = value;
private string currentValueOverlay = "";
private bool activeSearch = false;
private bool displaySearch = false;
private Timer debounceTimer;
private string searchString
get
return searchText;
set
searchText = value;
if (value.Length == 0)
debounceTimer.Stop();
activeSearch = false;
else
debounceTimer.Stop();
debounceTimer.Start();
activeSearch = true;
private string searchText = "";
private string errorMessage;
private List<Employee> foundEmployees;
protected override void OnParametersSet()
base.OnParametersSet();
debounceTimer = new Timer();
debounceTimer.Interval = Debounce;
debounceTimer.AutoReset = false;
debounceTimer.Elapsed += search;
protected override bool TryParseValueFromString(string value, out List<Employee> result, out string validationErrorMessage)
result = new List<Employee>();
validationErrorMessage = null;
string[] valueArray = value.Split(";");
List<Employee> output = new List<Employee>();
foreach (string employeeEmail in valueArray)
try
Employee employee = Task.Run(async () => await EmployeeDataService.FindEmployeesAsync(employeeEmail.Trim())).Result.FirstOrDefault();
if (employee != null)
output.Add(employee);
catch
validationErrorMessage = $"User \"employeeEmail\" was not found.";
result = output;
return true;
protected override string FormatValueAsString(List<Employee> employees)
string employeeString = "";
if (employees.Any() && employees.FirstOrDefault() != null)
employeeString = String.Join("; ", employees.Select(x => x.Email).ToArray());
return employeeString;
private async void toggleSearch()
displaySearch = !displaySearch;
await InvokeAsync(StateHasChanged);
private void openSearch()
foundEmployees = new List<Employee>();
if (searchString.Length >= MinimumLength)
search(null, null);
toggleSearch();
#nullable enable
private async void search(Object? source, ElapsedEventArgs? e)
errorMessage = null;
foundEmployees = null;
await InvokeAsync(StateHasChanged);
if (int.TryParse(searchString, out int i))
if (searchString.Length == 9)
List<string> searchList = new List<string>();
searchList.Add(searchString);
foundEmployees = (await EmployeeDataService.GetEmployeesAsync(searchList)).ToList();
else
updateError($"Searching by employee number requires the full number.");
else
if (searchString.Length < MinimumLength)
updateError($"You must enter at least MinimumLength characters of their name or email address.");
else
foundEmployees = (await EmployeeDataService.FindEmployeesAsync(searchString)).Take(MaximumResults).ToList();
if (foundEmployees != null && !foundEmployees.Any())
updateError($"No employees found matching \"searchString\".");
activeSearch = false;
await InvokeAsync(StateHasChanged);
private async void updateError(string message)
activeSearch = false;
errorMessage = message;
await InvokeAsync(StateHasChanged);
private void select(Employee employee)
Value.Add(employee);
【问题讨论】:
您介意共享自定义输入控件的代码及其 html 吗? 目前我发现的唯一解决方法是将 InputBase.cs 的源代码复制到自定义类 InputBaseVirtual.cs 中,然后将 Value 属性设为虚拟。稍后,从这个其他类继承。 【参考方案1】:在我看来你想限制这种方法
private void select(Employee employee)
if (MultiSelect)
Value.Add(employee);
else
Value = new List<Employee> employee ;
【讨论】:
我已经考虑过这一点,但是我需要将验证添加到与 Value 交互的任何地方,例如在字符串转换方法中。必须有一种方法可以跟踪 Value(我认为这是组件数据存储的最低级别)并在那里进行验证。【参考方案2】:要在自定义控件中添加验证消息,您可以覆盖 InputBase
上的 TryParseValueFromString
方法并编写如下:
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
if (typeof(TValue) == typeof(string))
result = (TValue)(object)value;
validationErrorMessage = null;
return true;
else if (typeof(TValue).IsEnum)
var success = BindConverter.TryConvertTo<TValue>(value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
result = parsedValue;
validationErrorMessage = null;
return true;
else
result = default;
validationErrorMessage = $"The FieldIdentifier.FieldName field is not valid.";
return false;
throw new InvalidOperationException($"GetType() does not support the type 'typeof(TValue)'.");
这取自 Chris Sainty 的 this blog 并提供了更多详细信息。
【讨论】:
将我的逻辑添加到这些方法中,确实在文本框中显示了正确的值。但是,输入组件的基础值仍然包含许多员工,这些员工将被保存回数据库。我需要管理组件 Value 以确保它始终正确。以上是关于如何在 Blazor 中覆盖 InputBase<T> 值,以进行验证的主要内容,如果未能解决你的问题,请参考以下文章
如何更改从 inputBase 继承的选择组件的 CSS、概述及其伪类
React Material中InputBase中的边框颜色