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:
三寸月光 2024-08-06 23:13:28 +08:00 коммит произвёл GitHub
Родитель 1bb97814ca
Коммит d6d45054be
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
10 изменённых файлов: 227 добавлений и 29 удалений

Просмотреть файл

@ -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");
}
}