feat(module: form): Add support to set ValidationMessages. (#4014)
* fix(module: form): invoke BuildEditContext,but formitem's CurrentEditContext is not update (#4013) * feat(module:form): Add support to update model's ValidationMessages. (#4006) * refactor * update demo * update demo * fix test --------- Co-authored-by: James Yeung <shunjiey@hotmail.com>
This commit is contained in:
Родитель
1bb97814ca
Коммит
d6d45054be
|
@ -188,7 +188,20 @@ namespace AntDesign
|
|||
FormValidateMode IForm.ValidateMode => ValidateMode;
|
||||
FormLocale IForm.Locale => Locale;
|
||||
|
||||
public event Action<IForm> OnFinishEvent;
|
||||
private event Action<IForm> OnFinishEvent;
|
||||
|
||||
event Action<IForm> IForm.OnFinishEvent
|
||||
{
|
||||
add
|
||||
{
|
||||
OnFinishEvent += value;
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
OnFinishEvent -= value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
|
@ -366,6 +379,7 @@ namespace AntDesign
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public void ValidationReset() => BuildEditContext();
|
||||
|
||||
public EditContext EditContext => _editContext;
|
||||
|
@ -400,6 +414,10 @@ namespace AntDesign
|
|||
}
|
||||
}
|
||||
_editContext = newContext;
|
||||
|
||||
// because EditForm's editcontext CascadingValue is fixed,so there need invoke StateHasChanged,
|
||||
// otherwise, the child component's(FormItem) EditContext will not update.
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private static BindingFlags AllBindings
|
||||
|
@ -427,5 +445,14 @@ namespace AntDesign
|
|||
}
|
||||
return _eventInfos;
|
||||
}
|
||||
|
||||
public void SetValidationMessages(string field, string[] errorMessages)
|
||||
{
|
||||
var fieldIdentifier = _editContext.Field(field);
|
||||
var formItem = _formItems
|
||||
.FirstOrDefault(t => t.GetFieldIdentifier().Equals(fieldIdentifier));
|
||||
|
||||
formItem?.SetValidationMessage(errorMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,6 +194,7 @@ namespace AntDesign
|
|||
private Action _vaildateStatusChanged;
|
||||
|
||||
private Action _nameChanged;
|
||||
private Action<string[]> _onValidated;
|
||||
|
||||
private string _name;
|
||||
|
||||
|
@ -358,12 +359,34 @@ namespace AntDesign
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void UpdateValidateMessage()
|
||||
{
|
||||
if (_control == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_validationMessages = CurrentEditContext.GetValidationMessages(_fieldIdentifier).Distinct().ToArray();
|
||||
_isValid = !_validationMessages.Any();
|
||||
|
||||
_validateStatus = _isValid ? _originalValidateStatus ?? FormValidateStatus.Default : FormValidateStatus.Error;
|
||||
|
||||
_onValidated(_validationMessages);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Help))
|
||||
{
|
||||
_validationMessages = new[] { Help };
|
||||
}
|
||||
_vaildateStatusChanged?.Invoke();
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
void IFormItem.AddControl<TValue>(AntInputComponentBase<TValue> control)
|
||||
{
|
||||
if (_control != null) return;
|
||||
|
||||
_vaildateStatusChanged = control.UpdateStyles;
|
||||
_nameChanged = control.OnNameChanged;
|
||||
_onValidated = control.OnValidated;
|
||||
|
||||
if (control.FieldIdentifier.Model == null)
|
||||
{
|
||||
|
@ -374,29 +397,11 @@ namespace AntDesign
|
|||
_fieldIdentifier = control.FieldIdentifier;
|
||||
this._control = control;
|
||||
|
||||
void ValidateDefault()
|
||||
{
|
||||
_validationMessages = CurrentEditContext.GetValidationMessages(control.FieldIdentifier).Distinct().ToArray();
|
||||
_isValid = !_validationMessages.Any();
|
||||
|
||||
_validateStatus = _isValid ? _originalValidateStatus ?? FormValidateStatus.Default : FormValidateStatus.Error;
|
||||
|
||||
control.OnValidated(_validationMessages);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Help))
|
||||
{
|
||||
_validationMessages = new[] { Help };
|
||||
}
|
||||
|
||||
_vaildateStatusChanged?.Invoke();
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
if (Form?.ValidateOnChange == true)
|
||||
{
|
||||
_validationStateChangedHandler = (s, e) =>
|
||||
{
|
||||
ValidateDefault();
|
||||
UpdateValidateMessage();
|
||||
};
|
||||
CurrentEditContext.OnValidationStateChanged += _validationStateChangedHandler;
|
||||
}
|
||||
|
@ -404,7 +409,7 @@ namespace AntDesign
|
|||
{
|
||||
_validationRequestedHandler = (s, e) =>
|
||||
{
|
||||
ValidateDefault();
|
||||
UpdateValidateMessage();
|
||||
};
|
||||
CurrentEditContext.OnValidationRequested += _validationRequestedHandler;
|
||||
}
|
||||
|
@ -472,6 +477,18 @@ namespace AntDesign
|
|||
|
||||
FieldIdentifier IFormItem.GetFieldIdentifier() => _fieldIdentifier;
|
||||
|
||||
void IFormItem.SetValidationMessage(string[] errorMessages)
|
||||
{
|
||||
_validationMessages = errorMessages;
|
||||
_isValid = !errorMessages.Any();
|
||||
_validateStatus = _isValid ? FormValidateStatus.Default : FormValidateStatus.Error;
|
||||
|
||||
_onValidated(_validationMessages);
|
||||
|
||||
_vaildateStatusChanged?.Invoke();
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private IEnumerable<FormValidationRule> GetRulesFromAttributes()
|
||||
{
|
||||
var attributes = _propertyReflector?.ValidationAttributes;
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace AntDesign
|
|||
|
||||
internal AntLabelAlignType? LabelAlign { get; }
|
||||
|
||||
internal EditContext EditContext { get; }
|
||||
|
||||
internal FormValidateMode ValidateMode { get; }
|
||||
|
||||
internal string Size { get; }
|
||||
|
@ -34,18 +32,52 @@ namespace AntDesign
|
|||
|
||||
internal FormLocale Locale { get; }
|
||||
|
||||
event Action<IForm> OnFinishEvent;
|
||||
internal event Action<IForm> OnFinishEvent;
|
||||
|
||||
|
||||
internal FormRequiredMark RequiredMark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The data object that the form is bound to.
|
||||
/// </summary>
|
||||
public object Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the form.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the current EditContext from the Form.
|
||||
/// </summary>
|
||||
EditContext EditContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the form has been modified.
|
||||
/// </summary>
|
||||
bool IsModified { get; }
|
||||
|
||||
string Name { get; }
|
||||
object Model { get; }
|
||||
FormRequiredMark RequiredMark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reset the values and validation messages of all fields.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Trigger `OnFinish` while all fields are valid, otherwise, trigger `OnFinishFailed`.
|
||||
/// </summary>
|
||||
void Submit();
|
||||
|
||||
/// <summary>
|
||||
/// Validate all fields.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool Validate();
|
||||
|
||||
/// <summary>
|
||||
/// Set validation messages for a specific field.
|
||||
/// </summary>
|
||||
/// <param name="field"></param>
|
||||
/// <param name="errorMessages"></param>
|
||||
void SetValidationMessages(string field, string[] errorMessages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,5 +23,7 @@ namespace AntDesign.Internal
|
|||
internal ValidationResult[] ValidateField();
|
||||
|
||||
internal FieldIdentifier GetFieldIdentifier();
|
||||
|
||||
internal void SetValidationMessage(string[] errorMessages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
@using System.ComponentModel.DataAnnotations;
|
||||
@using System.Text.Json;
|
||||
@using System.ComponentModel
|
||||
|
||||
<Form Model="@_model"
|
||||
OnFinish="OnFinish"
|
||||
LabelColSpan="8"
|
||||
WrapperColSpan="16"
|
||||
@ref="_form">
|
||||
<FormItem Label="User Name">
|
||||
<Input @bind-Value="@context.Username" />
|
||||
</FormItem>
|
||||
<FormItem WrapperColOffset="8" WrapperColSpan="16">
|
||||
<Button Type="@ButtonType.Primary" HtmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button Type="@ButtonType.Primary" HtmlType="button">
|
||||
Clear
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
@code
|
||||
{
|
||||
public class Model
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
}
|
||||
|
||||
private Model _model = new Model();
|
||||
Form<Model> _form;
|
||||
|
||||
private async Task OnFinish(EditContext editContext)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
|
||||
// occurs error from server
|
||||
_form?.SetValidationMessages("Username", ["Username has error."]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
order: 104
|
||||
title:
|
||||
zh-CN: 设置验证信息
|
||||
en-US: Setting validation messages
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可在任何时候设置验证信息。
|
||||
|
||||
## en-US
|
||||
|
||||
You can set validation messages at any time.
|
|
@ -127,6 +127,20 @@ FormItem can be automatically generated by the 'TItem' type. currently supports
|
|||
| NotGenerate | A generic delegate with two parameters, where the first parameter is of type PropertyInfo, the second parameter is of type TItem, and the return value is of type bool. If the delegate returns true, automatic generation will be skipped. | Func<PropertyInfo, TItem, bool>? | null |
|
||||
| SubformStyle | Nested form style, default to Collapse style, optional Block style.. | string | Collapse |
|
||||
|
||||
### Reference
|
||||
|
||||
IForm
|
||||
|
||||
| Name | Description |
|
||||
| ------------------------------------------------------------ | --------------------------------------- |
|
||||
| EditContext | Get the current EditContext from the Form. |
|
||||
| IsModified | Whether the form has been modified. |
|
||||
| Model | The data object that the form is bound to. |
|
||||
| Name | The name of the form. |
|
||||
| Reset() | Reset the values and validation messages of all fields. |
|
||||
| SetValidationMessages(string field, string[] errorMessages) | Set validation messages for a specific field. |
|
||||
| Submit() | Trigger `OnFinish` while all fields are valid, otherwise, trigger `OnFinishFailed`. |
|
||||
| Validate() | Validate all fields. |
|
||||
|
||||
|
||||
<style>
|
||||
|
|
|
@ -129,6 +129,22 @@ var formConfig = new FormConfig {
|
|||
| SubformStyle | 嵌套表单风格,默认为Collapse风格,可选Block风格 | string | Collapse |
|
||||
|
||||
|
||||
### 引用实例
|
||||
|
||||
IForm
|
||||
|
||||
| Name | Description |
|
||||
| ----------------------------------------------------------- | --------------------------------------- |
|
||||
| EditContext | 获取 Form 当前的 EditContext |
|
||||
| IsModified | 表单值是否被修改过 |
|
||||
| Model | Form 绑定的数据对象 |
|
||||
| Name | 表单名称 |
|
||||
| Reset() | 重置表单值和验证信息 |
|
||||
| SetValidationMessages(string field, string[] errorMessages) | 给指定的字段设置验证信息 |
|
||||
| Submit() | 当验证通过会触发 OnFinish,验证不通过则触发 OnFinishFailed |
|
||||
| Validate() | 验证所有字段 |
|
||||
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-form:not(.ant-form-inline):not(.ant-form-vertical) {
|
||||
max-width: 600px;
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace AntDesign.Tests.Form.Validation
|
|||
.Add(x => x.RequiredMark, requiredMark)
|
||||
.Add(x => x.ValidateMode, FormValidateMode.Rules)
|
||||
.Add(x => x.Model, new { }));
|
||||
|
||||
|
||||
wrappedSystemUnderTest.WaitForAssertion(
|
||||
() => wrappedSystemUnderTest
|
||||
.FindComponent<FormItem>()
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
@inherits AntDesignTestBase
|
||||
|
||||
@code {
|
||||
class Model
|
||||
{
|
||||
public string? ValidateField { get; set; }
|
||||
}
|
||||
Model _model = new();
|
||||
Form<Model>? form;
|
||||
FormItem? formItem;
|
||||
ValidationMessageStore? validationMessageStore;
|
||||
|
||||
[Fact]
|
||||
public void Form_UpdateValidateMessage()
|
||||
{
|
||||
//Arrange
|
||||
JSInterop.Setup<AntDesign.JsInterop.Window>(JSInteropConstants.GetWindow)
|
||||
.SetResult(new AntDesign.JsInterop.Window());
|
||||
var cut = Render(
|
||||
@<Form Model="@_model" @ref="form">
|
||||
<FormItem @ref="formItem"> <AntDesign.Input @bind-Value=@_model.ValidateField /></FormItem>
|
||||
<FormItem >
|
||||
<Button Type="@ButtonType.Primary" HtmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
|
||||
form.SetValidationMessages(nameof(_model.ValidateField), ["Error message"]);
|
||||
cut.Find(".ant-form-item-explain-error").Text().Trim().Should().Be("Error message");
|
||||
|
||||
form.SetValidationMessages(nameof(_model.ValidateField), ["New Error message"]);
|
||||
cut.Find(".ant-form-item-explain-error").Text().Trim().Should().Be("New Error message");
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче