Ole*_*ers 7 validation razor blazor blazor-server-side
我有一个使用编辑上下文的编辑表单:
<EditForm OnValidSubmit="HandleValidSubmit" EditContext="_editContext" Context="auth">
<DataAnnotationsValidator />
<input type="time" @bind-value="_foodTruck.EndDelivery" @onkeydown="@(q=>ResetValidation("EndDelivery"))" >
<ValidationMessage For="() => _foodTruck.EndDelivery" />
<input type="time" @bind-value="_foodTruck.StartDelivery" @onkeydown="@(q=>ResetValidation("StartDelivery"))" >
<ValidationMessage For="() => _foodTruck.StartDelivery" />
<input class="btn btn-default" type="submit" value="save" />
</EditForm>
Run Code Online (Sandbox Code Playgroud)
我在 HandleValidSubmit 中做了一些自定义验证:
EditContext _editContext = new EditContext(_foodTruck);
private async void HandleValidSubmit()
{
var messageStore = new ValidationMessageStore(_editContext);
if (_foodTruck.StartDelivery >= _foodTruck.EndDelivery)
{
messageStore.Add(_editContext.Field("EndDelivery"), "Bad time entered");
_editContext.NotifyValidationStateChanged();
}
if (!_editContext.Validate()) return;
}
Run Code Online (Sandbox Code Playgroud)
现在发生的是我的自定义错误(“输入的错误时间”)显示在正确的位置。唯一的问题是:当我更改值时,该错误不会消失。因此,如果我单击提交按钮,则永远不会再次调用 HandleValidSubmit。
我还尝试在修改字段时清空验证错误:
protected void ResetValidation(string field)
{
var messageStore = new ValidationMessageStore(_editContext);
messageStore.Clear(_editContext.Field(field));
messageStore.Clear();
_editContext.NotifyValidationStateChanged();
}
Run Code Online (Sandbox Code Playgroud)
这由 调用onkeydown
。但这似乎也没有效果。Errormessage 不会消失,因此HandleValidSubmit
也不会被调用。
我遇到了与原始海报相同的问题,因此我决定研究一下 EditContext 的源代码(谢谢 source.dot.net!)。因此,我提出了一种解决方法,该解决方法应该足以满足 Blazor 团队在未来版本中正确解决该问题的要求。
/// <summary>
/// Contains extension methods for working with the <see cref="EditForm"/> class.
/// </summary>
public static class EditFormExtensions
{
/// <summary>
/// Clears all validation messages from the <see cref="EditContext"/> of the given <see cref="EditForm"/>.
/// </summary>
/// <param name="editForm">The <see cref="EditForm"/> to use.</param>
/// <param name="revalidate">
/// Specifies whether the <see cref="EditContext"/> of the given <see cref="EditForm"/> should revalidate after all validation messages have been cleared.
/// </param>
/// <param name="markAsUnmodified">
/// Specifies whether the <see cref="EditContext"/> of the given <see cref="EditForm"/> should be marked as unmodified.
/// This will affect the assignment of css classes to a form's input controls in Blazor.
/// </param>
/// <remarks>
/// This extension method should be on EditContext, but EditForm is being used until the fix for issue
/// <see href="https://github.com/dotnet/aspnetcore/issues/12238"/> is officially released.
/// </remarks>
public static void ClearValidationMessages(this EditForm editForm, bool revalidate = false, bool markAsUnmodified = false)
{
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
object GetInstanceField(Type type, object instance, string fieldName)
{
var fieldInfo = type.GetField(fieldName, bindingFlags);
return fieldInfo.GetValue(instance);
}
var editContext = editForm.EditContext == null
? GetInstanceField(typeof(EditForm), editForm, "_fixedEditContext") as EditContext
: editForm.EditContext;
var fieldStates = GetInstanceField(typeof(EditContext), editContext, "_fieldStates");
var clearMethodInfo = typeof(HashSet<ValidationMessageStore>).GetMethod("Clear", bindingFlags);
foreach (DictionaryEntry kv in (IDictionary)fieldStates)
{
var messageStores = GetInstanceField(kv.Value.GetType(), kv.Value, "_validationMessageStores");
clearMethodInfo.Invoke(messageStores, null);
}
if (markAsUnmodified)
editContext.MarkAsUnmodified();
if (revalidate)
editContext.Validate();
}
}
Run Code Online (Sandbox Code Playgroud)
我通过在验证重置时创建一个新的 EditContext 解决了这个问题。所以我只是在 ResetValidation-Method 中添加了以下行:
_editContext = new EditContext(_foodTruck);
Run Code Online (Sandbox Code Playgroud)
但老实说:那感觉不对。因此,我将对此保持开放,以便获得更好的答案(希望如此)。
我有同样的问题。我找不到简单的解决方案。类似于下面的解决方法对我有用。
修改 EditForm 如下 -
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit">
Run Code Online (Sandbox Code Playgroud)
@代码块
EditContext _editContext;
ValidationMessageStore msgStore;
FoodTruck _foodTruck= new FoodTruck();
protected override void OnInitialized()
{
_editContext = new EditContext(_foodTruck);
msgStore = new ValidationMessageStore(_editContext);
}
void HandleSubmit()
{
msgStore.Clear();
if(_editContext.Validate()) // <-- Model Validation
{
if (_foodTruck.StartDelivery >= _foodTruck.EndDelivery) //<--Custom validation
{
msgStore = new ValidationMessageStore(_editContext);
msgStore.Add(_editContext.Field("EndDelivery"), "Bad time entered");
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于这个问题仍然出现在搜索中并且人们正在引用它,因此这个答案解释了问题存在的原因并展示了如何解决它。
让我们看看各种答案并消除一些都市神话和巫术:
重置EditContext
不是答案,只是一个巫术修复。它破坏的东西比修复的东西多:EditContext
除非你真的知道自己在做什么,否则永远不应该重置。
调用StateHasChanged
通常是基本逻辑设计有缺陷时强制UI更新的无奈之举。如果你必须编码,那么StateHasChanged
你需要认真问自己:为什么?
其他答案都是黑客。它们在某些情况下可以工作,但不能保证您的设计。
问题的根本原因是对aValidationMessageStore
是什么以及如何使用和管理它的误解。
ValidationMessageStore
比第一次出现的情况要复杂一些。它不是一个保存从各种来源记录的所有验证消息的存储:_messageStore = new ValidationMessageStore(_editContext);
应该是一个线索,特别是new
。您应该在实例化组件时获取实例,然后向该实例添加消息并清除该实例中的消息。每次调用方法时都创建一个根本行不通。您只是创建一个新的空ValidationMessageStore
.
这是问题中代码的工作版本:
@page "/"
<PageTitle>Index</PageTitle>
@if (loaded)
{
<EditForm OnValidSubmit="HandleValidSubmit" EditContext="_editContext" Context="auth">
<DataAnnotationsValidator />
<div class="p-2">
End Delivery
<input type="time" @bind-value="_foodTruck.EndDelivery" @onkeydown="@(()=>ResetValidation("EndDelivery"))">
<ValidationMessage For="() => _foodTruck.EndDelivery" />
</div>
<div class="p-2">
Start Delivery
<input type="time" @bind-value="_foodTruck.StartDelivery" @onkeydown="@(()=>ResetValidation("StartDelivery"))">
<ValidationMessage For="() => _foodTruck.StartDelivery" />
</div>
<div class="p-2 text-end">
<input class="btn btn-primary" type="submit" value="save" />
</div>
<div class="p-2 text-end">
Counter: @counter
</div>
</EditForm>
}
@code {
private FoodTruck _foodTruck = new FoodTruck();
private EditContext? _editContext;
private ValidationMessageStore? _messageStore;
private ValidationMessageStore messageStore => _messageStore!;
private int counter;
private bool loaded;
protected override async Task OnInitializedAsync()
{
// emulate gwtting some async data
await Task.Delay(100);
FoodTruck _foodTruck = new FoodTruck();
// assign the mdel data to the Edit Context
_editContext = new EditContext(_foodTruck);
// Get the ValidationMessageStore
_messageStore = new ValidationMessageStore(_editContext);
loaded = true;
}
private void HandleValidSubmit()
{
if (_editContext is not null)
{
// create a FieldIdentifier for EndDelivery
var fi = new FieldIdentifier(_foodTruck, "EndDelivery");
// Clear the specific entry from the message store using the FieldIdentifier
messageStore.Clear(fi);
if (_foodTruck.StartDelivery >= _foodTruck.EndDelivery)
{
// Add a validation message and raise the validation state change event
messageStore.Add(fi, "Bad time entered");
_editContext.NotifyValidationStateChanged();
}
}
}
protected void ResetValidation(string field)
{
counter++;
if (_editContext is not null)
{
// clear the validation message and raise the validation state change event
messageStore.Clear(new FieldIdentifier(_foodTruck, field));
_editContext.NotifyValidationStateChanged();
}
}
public class FoodTruck
{
public TimeOnly EndDelivery { get; set; }
public TimeOnly StartDelivery { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
在事件操作的末尾添加 this.StateHasChanged() ,以便它可以再次渲染 ui 元素并删除验证消息。
EditContext _editContext = new EditContext(_foodTruck);
private async void HandleValidSubmit()
{
var messageStore = new ValidationMessageStore(_editContext);
if (_foodTruck.StartDelivery >= _foodTruck.EndDelivery)
{
messageStore.Add(_editContext.Field("EndDelivery"), "Bad time entered");
_editContext.NotifyValidationStateChanged();
this.StateHasChanged(); //this line
}
if (!_editContext.Validate()) return;
}
Run Code Online (Sandbox Code Playgroud)
对于另一个
protected void ResetValidation(string field)
{
var messageStore = new ValidationMessageStore(_editContext);
messageStore.Clear(_editContext.Field(field));
messageStore.Clear();
_editContext.NotifyValidationStateChanged();
this.StateHasChanged(); //this line
}
Run Code Online (Sandbox Code Playgroud)
请告诉我是否有效
归档时间: |
|
查看次数: |
5135 次 |
最近记录: |