From 8ffc818f77c3cbcfedd7bd491d92748e9f263009 Mon Sep 17 00:00:00 2001 From: capdiem Date: Thu, 8 Aug 2024 14:40:57 +0800 Subject: [PATCH] (Form): optimize default behavior, add demos and update docs (#2084) * (Form): optimize default behavior, add demos and update docs * update --- .../Examples/components/forms/AutoLabel.razor | 45 +++++++++++++ .../components/forms/EnableI18n.razor | 6 +- .../forms/ValidateComplexType.razor | 65 ------------------- .../components/forms/ValidateOnDemo.razor | 59 +++++++++++++++++ docs/Masa.Blazor.Docs/Masa.Blazor.Docs.csproj | 1 - .../wwwroot/data/apis/forms/MForm-en-US.json | 4 +- .../wwwroot/data/apis/forms/MForm-zh-CN.json | 4 +- .../wwwroot/pages/components/forms/en-US.md | 33 +++++----- .../wwwroot/pages/components/forms/zh-CN.md | 49 +++++++------- .../getting-started/upgrade-guide/en-US.md | 38 ++++++++++- .../getting-started/upgrade-guide/zh-CN.md | 38 ++++++++++- .../Components/Form/AutoLabelOptions.cs | 6 +- src/Masa.Blazor/Components/Form/MForm.razor | 2 +- .../Components/Form/MForm.razor.cs | 21 ++++-- 14 files changed, 248 insertions(+), 123 deletions(-) create mode 100644 docs/Masa.Blazor.Docs/Examples/components/forms/AutoLabel.razor delete mode 100644 docs/Masa.Blazor.Docs/Examples/components/forms/ValidateComplexType.razor create mode 100644 docs/Masa.Blazor.Docs/Examples/components/forms/ValidateOnDemo.razor diff --git a/docs/Masa.Blazor.Docs/Examples/components/forms/AutoLabel.razor b/docs/Masa.Blazor.Docs/Examples/components/forms/AutoLabel.razor new file mode 100644 index 0000000000..0bd7abed57 --- /dev/null +++ b/docs/Masa.Blazor.Docs/Examples/components/forms/AutoLabel.razor @@ -0,0 +1,45 @@ +@using System.ComponentModel.DataAnnotations +@using System.ComponentModel + + + + + + + Submit + + @* *@ + + +@code { + + class Model + { + [Display(Name = "Username")] + [Required] + [MaxLength(10, ErrorMessage = "Name must be less than 10 characters")] + public string Name { get; set; } + + [Display(Name = "Favorite sport")] + [Required] public string Item { get; set; } + } + + private bool _valid = true; + private Model _model = new(); + + List _sports = new() + { + "Basketball", + "Football", + "Tennis", + "Swimming" + }; + +} \ No newline at end of file diff --git a/docs/Masa.Blazor.Docs/Examples/components/forms/EnableI18n.razor b/docs/Masa.Blazor.Docs/Examples/components/forms/EnableI18n.razor index 3d6af41187..1affe3a4eb 100644 --- a/docs/Masa.Blazor.Docs/Examples/components/forms/EnableI18n.razor +++ b/docs/Masa.Blazor.Docs/Examples/components/forms/EnableI18n.razor @@ -25,17 +25,17 @@ private class Model { - [DisplayName("enableI18n-demo.name")] + [Display(Name = "enableI18n-demo.name")] [Required(ErrorMessage = "enableI18n-demo.required")] [MaxLength(10, ErrorMessage = "enableI18n-demo.maxlength-10")] public string? Name { get; set; } - [DisplayName("enableI18n-demo.email")] + [Display(Name = "enableI18n-demo.email")] [Required(ErrorMessage = "enableI18n-demo.required")] [EmailAddress(ErrorMessage = "enableI18n-demo.invalid-email")] public string? Email { get; set; } - [DisplayName("enableI18n-demo.item")] + [Display(Name = "enableI18n-demo.item")] [Required(ErrorMessage = "enableI18n-demo.required")] public string? Item { get; set; } diff --git a/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateComplexType.razor b/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateComplexType.razor deleted file mode 100644 index 820a1e598f..0000000000 --- a/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateComplexType.razor +++ /dev/null @@ -1,65 +0,0 @@ -@using System.ComponentModel.DataAnnotations -@using Microsoft.AspNetCore.Components.Forms - - - - - - @foreach (var person in _order.Persons) - { - - Person:@person.Id - - - - - - } - Validate - - -@code { - - public class Order - { - public int Id { get; set; } - - [Required] - public string Title { get; set; } - - [Range(0, 20.00)] - public decimal Price { get; set; } - - [ValidateComplexType] - public List Persons { get; set; } - } - - public class Person - { - public int Id { get; set; } - - [Required] - public string Name { get; set; } - - [Range(25, 100)] - public int Age { get; set; } - } - - private Order _order = new() - { - Persons = new List - { - new() { Id = 1 } - } - }; - - public async Task SubmitOrder(FormContext context) - { - var success = context.Validate(); - if (success) - { - // do something - } - } - -} diff --git a/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateOnDemo.razor b/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateOnDemo.razor new file mode 100644 index 0000000000..5bdf21d693 --- /dev/null +++ b/docs/Masa.Blazor.Docs/Examples/components/forms/ValidateOnDemo.razor @@ -0,0 +1,59 @@ +@using System.ComponentModel.DataAnnotations +@using System.ComponentModel + +
+ + + + + + + + + + + + + + + Submit + +
+@code { + + class Model + { + [Display(Name = "Username")] + [Required] + [MinLength(5)] + public string Name { get; set; } + + [Display(Name = "Favorite sport")] + [Required] public string Item { get; set; } + + [Display(Name = "Age")] + [Range(18, 60)] + public int Age { get; set; } + } + + private bool _valid = true; + private Model _model = new(); + private ValidateOn _validateOn; + + List _sports = new() + { + "Basketball", + "Football", + "Tennis", + "Swimming" + }; + +} \ No newline at end of file diff --git a/docs/Masa.Blazor.Docs/Masa.Blazor.Docs.csproj b/docs/Masa.Blazor.Docs/Masa.Blazor.Docs.csproj index aa884510b4..8e4350fa19 100644 --- a/docs/Masa.Blazor.Docs/Masa.Blazor.Docs.csproj +++ b/docs/Masa.Blazor.Docs/Masa.Blazor.Docs.csproj @@ -15,7 +15,6 @@ - diff --git a/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-en-US.json b/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-en-US.json index 063da13931..6158861275 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-en-US.json +++ b/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-en-US.json @@ -4,7 +4,9 @@ "enableValidation": "Enable form validation.", "model": "Objects that require form validation.", "readonly": "", - "value": "Whether the verification is successful." + "value": "Whether the verification is successful.", + "autoLabel": "Whether to automatically generate labels for form items.", + "validateOn": "Validation timing, options: input, blur, submit." }, "events": { "onInvalidSubmit": "", diff --git a/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-zh-CN.json b/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-zh-CN.json index b17aef5976..9a05eb3f14 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-zh-CN.json +++ b/docs/Masa.Blazor.Docs/wwwroot/data/apis/forms/MForm-zh-CN.json @@ -4,7 +4,9 @@ "enableValidation": "开启表单校验", "model": "需要表单校验的对象", "readonly": "", - "value": "是否校验成功" + "value": "是否校验成功", + "validateOn": "校验时机,可选输入时验证、失去光标时验证和提交时验证", + "autoLabel": "是否自动添加标签" }, "events": { "onInvalidSubmit": "", diff --git a/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/en-US.md b/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/en-US.md index 9f5560d59d..17323a5e84 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/en-US.md +++ b/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/en-US.md @@ -23,6 +23,19 @@ Rules allow you to apply custom validation on all form components. These are ver +#### Auto label {released-on=v1.7.0} + +When `AutoLabel` is `true`, **MForm** will automatically get the value of the `[Display]` or `[DisplayName]` attribute of the form field as the label. +By default, it resolves the `[Display]` attribute, and you can also configure the `AttributeType` to resolve the `[DisplayName]` attribute by setting the `Masa.Blazor.Components.Form.AutoLabelOptions` subcomponent. + + + +#### Validate on {released-on=v1.7.0} + +The `ValidateOn` prop is used to specify when to validate the form, with optional values of `Input`, `Blur` and `Submit`. + + + ### Misc #### Model validation @@ -37,31 +50,21 @@ Validate a single field using the `Validate` method of the **MForm** instance. -#### Validation with submit and clear {updated-in=v1.6.0} +#### Submit and clear {#submit-and-clear updated-in=v1.6.0} You can use the methods provided by `Context` in the content of **MForm**, or use the component instance provided by `@ref` outside of **MForm**. -#### Enable I18n {updated-in=v1.6.0} +#### DataAnnotations with I18n {#enable-i18n updated-in=v1.6.0} Enable [I18n](/blazor/features/internationalization) to support multilingual validation messages. Locale resources used in the example can be found in [GitHub](https://github.com/masastack/MASA.Blazor/blob/0f4a450479bceb816d58bbbb7b8f8ca7655e2f94/docs/Masa.Docs.Shared/wwwroot/locale/en-US.json#L128). - + -#### Validate complex type with DataAnnotations - -Use `[ValidateComplexType]` attribute and `` to validate complex types. - -```xml Project.csproj - -``` - - - -#### Validate with FluentValidation +#### Validate with FluentValidation {#fluent-validation} @@ -69,7 +72,7 @@ Use `[ValidateComplexType]` attribute and ` -#### Parse external validation result +#### Parse external validation result {#parse-form-validation} **MForm** supports parsing of `ValidationResult` , which users can use as `FormContext.ParseFormValidation' parameter that displays the validation results in a front-end form, using the validation collection as an example. diff --git a/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/zh-CN.md b/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/zh-CN.md index 8134e71621..58b7690e05 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/zh-CN.md +++ b/docs/Masa.Blazor.Docs/wwwroot/pages/components/forms/zh-CN.md @@ -7,61 +7,64 @@ related: - /blazor/components/text-fields --- -## 使用 +## 使用 {#usage} 内部的 **MForm** 组件可以很容易地向表单输入添加验证。所有输入组件都有一个 `Rules` 参数,该参数接受类型为 `Func` 的列表。这允许您指定输入有效或无效的条件。每当输入值改变时,数组中的每个函数都会收到一个新值,并且每个数组元素都会被评分。如果函数或数组元素返回 `false` 或 `string`,表示验证失败,则将字符串值显示为错误消息。 -## 示例 +## 示例 {#examples} -### 属性 +### 属性 {#props} -#### 规则 +#### 规则 {#rules} 规则允许您对所有表单组件应用自定义验证。这些是按顺序验证的,一次最多显示 1 个错误,因此请确保相应地对规则进行排序。 -### 其他 +#### 自动标签 {#auto-label released-on=v1.7.0} -#### 模型验证 +当 `AutoLabel` 为 `true` 时,**MForm** 会自动获取表单对象字段的 `[Display]` 或 `[DisplayName]` 特性的值作为 Label。 +默认解析 `[Display]` attribute,也可以通过 `Masa.Blazor.Components.Form.AutoLabelOptions` 子组件配置 `AttributeType` 为 `typeof(DisplayNameAttribute)` 解析 `[DisplayName]` attribute。 + + + +#### 验证时机 {#validate-on released-on=v1.7.0} + +`ValidateOn` 属性用于指定何时验证表单,可选值为 `Input`,`Blur` 和 `Submit`。 + + + +### 其他 {#misc} + +#### 模型验证 {#model-validation} 除了在每个输入组件上通过 `Rules` 属性进行验证之外,还可以对单个对象模型进行验证。 -#### 验证单个字段 {released-on=v1.6.0} +#### 验证单个字段 {#validate-single-field released-on=v1.6.0} -使用**MForm**实例的`Validate`方法验证单个字段。 +使用 **MForm** 实例的 `Validate` 方法验证单个字段。 -#### 通过提交和清除进行验证 {updated-in=v1.6.0} +#### 通过提交和清除进行验证 {#submit-and-clear updated-in=v1.6.0} 在 **MForm** 的内容里可以使用 `Context` 提供的方法,在 **MForm** 外部可以使用 `@ref` 拿到组件实例提供的方法。 -#### 启用 I18n {updated-in=v1.6.0} +#### DataAnnotations 本地化 {#enable-i18n updated-in=v1.7.0} 通过 `EnableI18n` 属性启用 [I18n](/blazor/features/internationalization) 以支持验证信息多语言。示例使用的本地话资源你能在 [GitHub](https://github.com/masastack/MASA.Blazor/blob/0f4a450479bceb816d58bbbb7b8f8ca7655e2f94/docs/Masa.Docs.Shared/wwwroot/locale/zh-CN.json#L129) 中找到。 - + -#### 通过 DataAnnotations 验证复杂类型 - -使用 `[ValidateComplexType]` 属性和 ``,可以验证复杂类型。 - -```xml Project.csproj - -``` - - - -#### 使用 FluentValidation 验证 +#### 使用 FluentValidation 验证 {#fluent-validation} **MForm** 支持 **FluentValidation** 验证。 @@ -69,7 +72,7 @@ related: -#### 解析外部验证结果 +#### 解析外部验证结果 {#parse-form-validation} **MForm** 支持解析 `ValidationResult`,用户可以将服务端表单验证返回的 `ValidationResult` 作为 `FormContext.ParseFormValidation` 的参数,将验证结果在前端表单展示,以验证集合为示例。 diff --git a/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/en-US.md b/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/en-US.md index 06db7975b2..f7693baefa 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/en-US.md +++ b/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/en-US.md @@ -2,9 +2,9 @@ ## Upgrading form v1.6.x to v1.7.0 -### Components +### Components {#v1-7-0-components} -#### Pagination +#### Pagination {#v1-7-0-pagination} A mini style UI has been added, now when the browser window is less than *600px*, it will automatically use it. If you don't want to use the mini style, you can manually set it through the `MiniVariant` property. @@ -15,6 +15,40 @@ A mini style UI has been added, now when the browser window is less than *600px* > ``` +#### Form {#v1-7-0-form} + +DataAnnotations validation now natively supports complex types, no need to reference additional libraries and code. + +```diff .csproj +- +``` + +```diff .razor + +- + @foreach (var person in _order.Persons) + { + + } + + + @code { + public class Order + { +- [ValidateComplexType] + public List Persons { get; set; } + } + + public class Person + { + [Required] + public string Name { get; set; } + } + + private Order _order = new() { Persons = [] }; + } +``` + ## Upgrading form v1.5.x to v1.6.0 ### Change the script diff --git a/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/zh-CN.md b/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/zh-CN.md index c7b49920ba..3d5d1752ac 100644 --- a/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/zh-CN.md +++ b/docs/Masa.Blazor.Docs/wwwroot/pages/getting-started/upgrade-guide/zh-CN.md @@ -2,9 +2,9 @@ ## 从 v1.6.x 升级到 v1.7.0 {#upgrading-from-v1-6-x-to-v1-7-0} -### 组件 {#components} +### 组件 {#v1-7-0-components} -#### Pagination +#### Pagination {#v1-7-0-pagination} 新增了一个迷你样式的UI,现在当浏览器窗口小于 *600px* 时,会自动使用。如果不想使用迷你样式,可以通过 `MiniVariant` 属性手动设置。 @@ -15,6 +15,40 @@ > ``` +#### Form {#v1-7-0-form} + +DataAnnotations 验证现在内置支持复杂类型,不需要引用额外的类库和代码。 + +```diff .csproj +- +``` + +```diff .razor + +- + @foreach (var person in _order.Persons) + { + + } + + + @code { + public class Order + { +- [ValidateComplexType] + public List Persons { get; set; } + } + + public class Person + { + [Required] + public string Name { get; set; } + } + + private Order _order = new() { Persons = [] }; + } +``` + ## 从 v1.5.x 升级到 v1.6.0 ### 更改脚本 diff --git a/src/Masa.Blazor/Components/Form/AutoLabelOptions.cs b/src/Masa.Blazor/Components/Form/AutoLabelOptions.cs index 3f228106a7..af36323f27 100644 --- a/src/Masa.Blazor/Components/Form/AutoLabelOptions.cs +++ b/src/Masa.Blazor/Components/Form/AutoLabelOptions.cs @@ -1,5 +1,4 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using IComponent = Microsoft.AspNetCore.Components.IComponent; namespace Masa.Blazor.Components.Form; @@ -17,7 +16,8 @@ public class AutoLabelOptions : IComponent /// Supported types are and . /// [Parameter] - public Type? AttributeType { get; set; } = typeof(DisplayNameAttribute); + [MasaApiParameter(nameof(DisplayAttribute))] + public Type? AttributeType { get; set; } = typeof(DisplayAttribute); private bool _init; diff --git a/src/Masa.Blazor/Components/Form/MForm.razor b/src/Masa.Blazor/Components/Form/MForm.razor index 61d1c1d2fb..b407e88a61 100644 --- a/src/Masa.Blazor/Components/Form/MForm.razor +++ b/src/Masa.Blazor/Components/Form/MForm.razor @@ -10,7 +10,7 @@ @attributes="@Attributes"> @ChildContent?.Invoke(FormContext!) - @if (AutoLabel) + @if (ComputedAutoLabel) { } diff --git a/src/Masa.Blazor/Components/Form/MForm.razor.cs b/src/Masa.Blazor/Components/Form/MForm.razor.cs index 501d5b2fa2..cc5f316fda 100644 --- a/src/Masa.Blazor/Components/Form/MForm.razor.cs +++ b/src/Masa.Blazor/Components/Form/MForm.razor.cs @@ -1,13 +1,22 @@ using System.Collections.Concurrent; -using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Reflection; using Masa.Blazor.Components.Form; using Masa.Blazor.Components.Input; +using ValidationResult = Masa.Blazor.Components.Form.ValidationResult; namespace Masa.Blazor; public partial class MForm : MasaComponentBase { + /// + /// Auto generate label for the form fields. + /// The default logic is to use the attribute. + /// + [Parameter] + [MasaApiParameter(ReleasedOn = "v1.7.0")] + public bool AutoLabel { get; set; } + [Parameter] public RenderFragment? ChildContent { get; set; } [Parameter] public EventCallback OnSubmit { get; set; } @@ -33,10 +42,8 @@ public partial class MForm : MasaComponentBase [Parameter] public EventCallback OnInvalidSubmit { get; set; } [Parameter] - [MasaApiParameter(true, ReleasedOn = "v1.7.0")] - public bool AutoLabel { get; set; } = true; - - [Parameter] public ValidateOn ValidateOn { get; set; } = ValidateOn.Input; + [MasaApiParameter(ReleasedOn = "v1.7.0")] + public ValidateOn ValidateOn { get; set; } = ValidateOn.Input; internal ConcurrentDictionary AutoLabelMap { get; } = new(); @@ -44,6 +51,8 @@ public partial class MForm : MasaComponentBase private object? _oldModel; + private bool ComputedAutoLabel => AutoLabel || EnableI18n; + public EditContext? EditContext { get; protected set; } public FormContext? FormContext { get; private set; } @@ -56,7 +65,7 @@ public partial class MForm : MasaComponentBase /// The type of property attribute used to generate the label, /// the default value is same as . /// - internal Type? LabelAttributeType { get; set; } = typeof(DisplayNameAttribute); + internal Type? LabelAttributeType { get; set; } = typeof(DisplayAttribute); protected override void OnParametersSet() {