在我的自定义 select blazor 组件中创建 selection 时回调
Callback when selection made inside my custom select blazor component
我正在研究 Blazor Wasm。我想创建自己的自定义 select
组件。下面的实现有效但缺少一些东西:当在我的自定义 select 组件中 selected 某些东西时没有回调。我希望能够从我的 TestView.razor 组件中捕获事件。
我的自定义实现InputSelectCustom.cs
public class InputSelectCustom<T> : InputSelect<T>
{
protected override string FormatValueAsString(T value)
{
// Custom code ommitted for clarity
return base.FormatValueAsString(value);
}
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
{
if (typeof(T) == typeof(int) ||
typeof(T) == typeof(int?))
{
if (int.TryParse(value, out var resultInt))
{
result = (T)(object)resultInt;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
if (typeof(T).IsEnum)
{
if (CustomFunctions.EnumTryParse<T>(value, out var resultEnum))
{
result = (T)(object)resultEnum;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
if (typeof(T).IsNullableEnum())
{
if (CustomFunctions.NullableEnumTryParse<T>(value, out var resultEnum))
{
result = (T)(object)resultEnum;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
{
return base.TryParseValueFromString(value, out result, out validationErrorMessage);
}
}
protected override Task OnParametersSetAsync()
{
return base.OnParametersSetAsync();
}
}
我的TestView.razor
@page "/test-view"
<EditForm Model="CurrentFilterModel">
<InputSelectCustom @bind-Value="@CurrentFilterModel.ClassmentFilter">
<option value="null"> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
</EditForm>
@code
{
public FilterModel CurrentFilterModel = new FilterModel();
public class FilterModel
{
public EnumClassment? ClassmentFilter { get; set; }
}
}
我的枚举:
public enum EnumClassment
{
Featured,
Popular,
Novelty
}
更新
所以我需要的是一种在我的 selected 值发生变化时得到通知的方法。
起初,我试过这个:
<InputSelectCustom @bind-Value="CurrentFilterModel.ClassmentFilter">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
所选值已正确更改,但使用上面的代码我无法收到值更改的通知。根据信息,我需要得到通知,因为每当用户更改 selection 时,我想立即执行服务器端调用以过滤数据。
下面是我的第二次尝试:
<InputSelectCustom @bind-Value="CurrentFilterModel.ClassmentFilter" ValueChanged="ValueChangedForClassmentFilter">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
如您所见,我不能同时使用 @bind-Value
和 ValueChanged
。
所以这是我的最终解决方案:
<InputSelectCustom Value="@CurrentFilterModel.ClassmentFilter" ValueExpression="@( () => CurrentFilterModel.ClassmentFilter )" ValueChanged="@( (EnumClassment? s) => ValueChangedForClassmentFilter(s) )">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
protected async Task ValueChangedForClassmentFilter(EnumClassment? theUserInput)
{
// You have to update the model manually because handling the ValueChanged event does not let you use @bind-Value
// For the validation to work you must now also define the ValueExpression because @bind-Value did it for you
CurrentFilterModel.ClassmentFilter = theUserInput;
// Refresh data based on filters
await FilterCoursesServerSide();
}
这一次,一切都按预期工作:每当用户 select 另一个值时,我都会收到新值的通知,我可以立即执行自定义代码。
如果我错了或者如果这可以以更优雅的方式完成,请纠正我,因为我是 Blazor 的新手,也许我错过了一些东西。
PS:使用字符串、整数和枚举进行测试。
你不能那样做,也不应该那样做。做什么的 ?
但是,您可以像使用单选按钮那样执行此操作,但是您的自定义组件应该派生自 InputBase,并提供 Razor 标记和逻辑。
there is no callback when something is selected inside my custom select component.
当然有回调...否则你的模型怎么能用新值更新。由InputBase组件的CurrentValue属性触发:
protected TValue CurrentValue
{
get => Value;
set
{
var hasChanged = !EqualityComparer<TValue>.Default.Equals(value,
Value);
if (hasChanged)
{
Value = value;
_ = ValueChanged.InvokeAsync(value);
EditContext.NotifyFieldChanged(FieldIdentifier);
}
}
}
这是更新模型的代码:
_ = ValueChanged.InvokeAsync(value);
这是您的自定义 select 组件的代码。请注意,InputSelect 不支持数字类型,例如 int(您不能将其应用于 int 属性,例如 public int Country { get; set; }
。也许(不确定,需要检查...)。枚举类型同样。我的组件支持这样的值:
InputSelectNumber.cs
public class InputSelectNumber<T> : InputSelect<T>
{
protected override bool TryParseValueFromString(string value, out T
result, out string validationErrorMessage)
{
if (typeof(T) == typeof(int))
{
if (int.TryParse(value, out var resultInt))
{
result = (T)(object)resultInt;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not a valid
number.";
return false;
}
}
else
{
return base.TryParseValueFromString(value, out result, out
validationErrorMessage);
}
}
}
用法
<EditForm EditContext="@EditContext">
<DataAnnotationsValidator />
<div class="form-group">
<label for="name">Enter your Name: </label>
<InputText Id="name" Class="form-control" @bind-Value="@comment.Name">
</InputText>
<ValidationMessage For="@(() => comment.Name)" />
</div>
<div class="form-group">
<label for="body">Select your country: </label>
<InputSelectNumber @bind-Value="@comment.Country">
<option value="">Select country...</option>
<option value="1">USA</option>
<option value="2">Britain</option>
<option value="3">Germany</option>
<option value="4">Israel</option>
</InputSelectNumber>
<label for="body">Select your country: </label>
<ValidationMessage For="@(() => comment.Country)" />
</div>
<p>
<button type="submit">Submit</button>
</p>
</EditForm>
<p>Name: @comment.Name</p>
<p>Country: @comment.Country</p>
@code
{
private EditContext EditContext;
private Comment comment = new Comment();
protected override void OnInitialized()
{
EditContext = new EditContext(comment);
}
public class Comment
{
public string Name { get; set; }
// Note that with the subclassed InputSelectNumber I can use either string
// or int. In both cases no error occurs.
public string Country { get; set; }
//public int Country { get; set; }
}
}
根据请求更新:
Please correct me if I'm wrong or if this can be done a more elegant way because I am new to Blazor and maybe I missed something.
您的代码很好,您应该这样做。 See my answer here 我对 InputSelect 组件执行完全相同的操作。由于您的自定义组件派生自 InputSelect 组件,因此这是最好、最优雅的实现方式。也许是唯一的办法。
你对 Blazor 有多陌生?;) 你的知识超过了这里的大多数用户(我指的是那些永远在这里回答问题的用户)。
但是,从表面上看,我认为您的方向是错误的。您不需要知道 select 组件的值何时改变了您的操作方式...
相反,您应该为 EditContext 对象公开的 OnFieldChanged 事件实现一个事件处理程序,您可以在其中访问通过事件参数传递的新值、操作数据等,甚至验证 EditContext 对象...
一些指导您的代码:
private EditContext EditContext;
private Comment Model = new Comment();
protected override void OnInitialized()
{
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
base.OnInitialized();
}
// Note: The OnFieldChanged event is raised for each field in the
model
private void EditContext_OnFieldChanged(object sender,
FieldChangedEventArgs e)
{
Console.WriteLine(e.FieldIdentifier.FieldName);
// more code...
}
希望这对您有所帮助...
我正在研究 Blazor Wasm。我想创建自己的自定义 select
组件。下面的实现有效但缺少一些东西:当在我的自定义 select 组件中 selected 某些东西时没有回调。我希望能够从我的 TestView.razor 组件中捕获事件。
我的自定义实现InputSelectCustom.cs
public class InputSelectCustom<T> : InputSelect<T>
{
protected override string FormatValueAsString(T value)
{
// Custom code ommitted for clarity
return base.FormatValueAsString(value);
}
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
{
if (typeof(T) == typeof(int) ||
typeof(T) == typeof(int?))
{
if (int.TryParse(value, out var resultInt))
{
result = (T)(object)resultInt;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
if (typeof(T).IsEnum)
{
if (CustomFunctions.EnumTryParse<T>(value, out var resultEnum))
{
result = (T)(object)resultEnum;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
if (typeof(T).IsNullableEnum())
{
if (CustomFunctions.NullableEnumTryParse<T>(value, out var resultEnum))
{
result = (T)(object)resultEnum;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not valid.";
return false;
}
}
else
{
return base.TryParseValueFromString(value, out result, out validationErrorMessage);
}
}
protected override Task OnParametersSetAsync()
{
return base.OnParametersSetAsync();
}
}
我的TestView.razor
@page "/test-view"
<EditForm Model="CurrentFilterModel">
<InputSelectCustom @bind-Value="@CurrentFilterModel.ClassmentFilter">
<option value="null"> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
</EditForm>
@code
{
public FilterModel CurrentFilterModel = new FilterModel();
public class FilterModel
{
public EnumClassment? ClassmentFilter { get; set; }
}
}
我的枚举:
public enum EnumClassment
{
Featured,
Popular,
Novelty
}
更新
所以我需要的是一种在我的 selected 值发生变化时得到通知的方法。
起初,我试过这个:
<InputSelectCustom @bind-Value="CurrentFilterModel.ClassmentFilter">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
所选值已正确更改,但使用上面的代码我无法收到值更改的通知。根据信息,我需要得到通知,因为每当用户更改 selection 时,我想立即执行服务器端调用以过滤数据。
下面是我的第二次尝试:
<InputSelectCustom @bind-Value="CurrentFilterModel.ClassmentFilter" ValueChanged="ValueChangedForClassmentFilter">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
如您所见,我不能同时使用 @bind-Value
和 ValueChanged
。
所以这是我的最终解决方案:
<InputSelectCustom Value="@CurrentFilterModel.ClassmentFilter" ValueExpression="@( () => CurrentFilterModel.ClassmentFilter )" ValueChanged="@( (EnumClassment? s) => ValueChangedForClassmentFilter(s) )">
<option value=""> Show all content </option>
<option value="@EnumClassment.Popular"> Popular </option>
<option value="@EnumClassment.Featured"> Featured </option>
<option value="@EnumClassment.Novelty"> Novelty </option>
</InputSelectCustom>
protected async Task ValueChangedForClassmentFilter(EnumClassment? theUserInput)
{
// You have to update the model manually because handling the ValueChanged event does not let you use @bind-Value
// For the validation to work you must now also define the ValueExpression because @bind-Value did it for you
CurrentFilterModel.ClassmentFilter = theUserInput;
// Refresh data based on filters
await FilterCoursesServerSide();
}
这一次,一切都按预期工作:每当用户 select 另一个值时,我都会收到新值的通知,我可以立即执行自定义代码。
如果我错了或者如果这可以以更优雅的方式完成,请纠正我,因为我是 Blazor 的新手,也许我错过了一些东西。
PS:使用字符串、整数和枚举进行测试。
你不能那样做,也不应该那样做。做什么的 ? 但是,您可以像使用单选按钮那样执行此操作,但是您的自定义组件应该派生自 InputBase,并提供 Razor 标记和逻辑。
there is no callback when something is selected inside my custom select component.
当然有回调...否则你的模型怎么能用新值更新。由InputBase组件的CurrentValue属性触发:
protected TValue CurrentValue
{
get => Value;
set
{
var hasChanged = !EqualityComparer<TValue>.Default.Equals(value,
Value);
if (hasChanged)
{
Value = value;
_ = ValueChanged.InvokeAsync(value);
EditContext.NotifyFieldChanged(FieldIdentifier);
}
}
}
这是更新模型的代码:
_ = ValueChanged.InvokeAsync(value);
这是您的自定义 select 组件的代码。请注意,InputSelect 不支持数字类型,例如 int(您不能将其应用于 int 属性,例如 public int Country { get; set; }
。也许(不确定,需要检查...)。枚举类型同样。我的组件支持这样的值:
InputSelectNumber.cs
public class InputSelectNumber<T> : InputSelect<T>
{
protected override bool TryParseValueFromString(string value, out T
result, out string validationErrorMessage)
{
if (typeof(T) == typeof(int))
{
if (int.TryParse(value, out var resultInt))
{
result = (T)(object)resultInt;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = "The chosen value is not a valid
number.";
return false;
}
}
else
{
return base.TryParseValueFromString(value, out result, out
validationErrorMessage);
}
}
}
用法
<EditForm EditContext="@EditContext">
<DataAnnotationsValidator />
<div class="form-group">
<label for="name">Enter your Name: </label>
<InputText Id="name" Class="form-control" @bind-Value="@comment.Name">
</InputText>
<ValidationMessage For="@(() => comment.Name)" />
</div>
<div class="form-group">
<label for="body">Select your country: </label>
<InputSelectNumber @bind-Value="@comment.Country">
<option value="">Select country...</option>
<option value="1">USA</option>
<option value="2">Britain</option>
<option value="3">Germany</option>
<option value="4">Israel</option>
</InputSelectNumber>
<label for="body">Select your country: </label>
<ValidationMessage For="@(() => comment.Country)" />
</div>
<p>
<button type="submit">Submit</button>
</p>
</EditForm>
<p>Name: @comment.Name</p>
<p>Country: @comment.Country</p>
@code
{
private EditContext EditContext;
private Comment comment = new Comment();
protected override void OnInitialized()
{
EditContext = new EditContext(comment);
}
public class Comment
{
public string Name { get; set; }
// Note that with the subclassed InputSelectNumber I can use either string
// or int. In both cases no error occurs.
public string Country { get; set; }
//public int Country { get; set; }
}
}
根据请求更新:
Please correct me if I'm wrong or if this can be done a more elegant way because I am new to Blazor and maybe I missed something.
您的代码很好,您应该这样做。 See my answer here 我对 InputSelect 组件执行完全相同的操作。由于您的自定义组件派生自 InputSelect 组件,因此这是最好、最优雅的实现方式。也许是唯一的办法。
你对 Blazor 有多陌生?;) 你的知识超过了这里的大多数用户(我指的是那些永远在这里回答问题的用户)。
但是,从表面上看,我认为您的方向是错误的。您不需要知道 select 组件的值何时改变了您的操作方式...
相反,您应该为 EditContext 对象公开的 OnFieldChanged 事件实现一个事件处理程序,您可以在其中访问通过事件参数传递的新值、操作数据等,甚至验证 EditContext 对象...
一些指导您的代码:
private EditContext EditContext;
private Comment Model = new Comment();
protected override void OnInitialized()
{
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
base.OnInitialized();
}
// Note: The OnFieldChanged event is raised for each field in the
model
private void EditContext_OnFieldChanged(object sender,
FieldChangedEventArgs e)
{
Console.WriteLine(e.FieldIdentifier.FieldName);
// more code...
}
希望这对您有所帮助...