From bb24becf8655bc6c9581f9cf57669f148e6ef5e8 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sat, 20 Mar 2021 15:56:39 +0800 Subject: [PATCH 01/12] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E7=BA=A7?= =?UTF-8?q?=E8=81=94=E5=8F=82=E6=95=B0=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BootstrapBlazorDataAnnotationsValidator.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorDataAnnotationsValidator.cs b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorDataAnnotationsValidator.cs index 66f78c0c7..b9e0451bf 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorDataAnnotationsValidator.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorDataAnnotationsValidator.cs @@ -4,11 +4,13 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; +using System; +using System.Diagnostics.CodeAnalysis; namespace BootstrapBlazor.Components { /// - /// DataAnnotationsValidator 验证组件 + /// BootstrapBlazorDataAnnotationsValidator 验证组件 /// public class BootstrapBlazorDataAnnotationsValidator : ComponentBase { @@ -29,7 +31,19 @@ namespace BootstrapBlazor.Components /// protected override void OnInitialized() { - CurrentEditContext!.AddEditContextDataAnnotationsValidation(EditForm!); + if (EditForm == null) + { + throw new InvalidOperationException($"{nameof(BootstrapBlazorDataAnnotationsValidator)} requires a cascading " + + $"parameter of type {nameof(ValidateForm)}. For example, you can use {nameof(BootstrapBlazorDataAnnotationsValidator)} " + + $"inside an {nameof(ValidateForm)}."); + } + + if (CurrentEditContext == null) + { + throw new InvalidOperationException($"{nameof(BootstrapBlazorDataAnnotationsValidator)} requires a cascading parameter of type {nameof(EditContext)}. For example, you can use {nameof(BootstrapBlazorDataAnnotationsValidator)} inside an EditForm."); + } + + CurrentEditContext.AddEditContextDataAnnotationsValidation(EditForm); } } } -- Gitee From c44dd1aeedfd4653a41b3c522e9146d434390144 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sat, 20 Mar 2021 16:35:22 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20Validate=20=E7=BB=84=E4=BB=B6=20i?= =?UTF-8?q?d=20=E7=94=9F=E6=88=90=E8=A7=84=E5=88=99=E5=A2=9E=E5=8A=A0=20Mo?= =?UTF-8?q?del=20=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Validate/ValidateBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs index 73216a508..8214d0e52 100644 --- a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs +++ b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs @@ -188,7 +188,7 @@ namespace BootstrapBlazor.Components [Parameter] public override string? Id { - get { return (EditForm != null && !string.IsNullOrEmpty(EditForm.Id) && FieldIdentifier != null) ? $"{EditForm.Id}_{FieldIdentifier.Value.FieldName}" : _id; } + get { return (EditForm != null && !string.IsNullOrEmpty(EditForm.Id) && FieldIdentifier != null) ? $"{EditForm.Id}_{FieldIdentifier.Value.Model.GetHashCode()}_{FieldIdentifier.Value.FieldName}" : _id; } set { _id = value; } } -- Gitee From 0136d664e2edc5ca19b02d65c81cd389fcf10e45 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 00:38:26 +0800 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20Lambda=20=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=A2=9E=E5=8A=A0=20GetPropertyValue=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/LambdaExtensions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/BootstrapBlazor/Extensions/LambdaExtensions.cs b/src/BootstrapBlazor/Extensions/LambdaExtensions.cs index d15f7143a..9fbad5222 100644 --- a/src/BootstrapBlazor/Extensions/LambdaExtensions.cs +++ b/src/BootstrapBlazor/Extensions/LambdaExtensions.cs @@ -3,6 +3,7 @@ // Website: https://www.blazor.zone or https://argozhang.github.io/ using BootstrapBlazor.Components; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -365,6 +366,21 @@ namespace System.Linq return Expression.Lambda>(body, param_p1, param_p2); } + private static readonly ConcurrentDictionary<(Type ModelType, string FieldName), Func> PropertyValueInvokerCache = new(); + + /// + /// 获取 指定对象的属性值 + /// + /// + /// + /// + public static object GetPropertyValue(object model, string fieldName) + { + var cacheKey = (model.GetType(), fieldName); + var invoker = PropertyValueInvokerCache.GetOrAdd(cacheKey, key => GetPropertyValueLambda(model, key.FieldName).Compile()); + return invoker.Invoke(model); + } + #region TryParse /// /// -- Gitee From 2e3de768a4de58f5134d7d57e378432c8b3353dd Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 00:39:03 +0800 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=E9=87=8D=E6=96=B0=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=8D=E6=9D=82=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Validate/ValidateBase.cs | 2 +- ...zorEditContextDataAnnotationsExtensions.cs | 98 +------------ .../ValidateForm/ValidateForm.razor.cs | 129 ++++++++++++++---- 3 files changed, 103 insertions(+), 126 deletions(-) diff --git a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs index 8214d0e52..1f5633e9d 100644 --- a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs +++ b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs @@ -342,7 +342,7 @@ namespace BootstrapBlazor.Components if (EditForm != null && FieldIdentifier.HasValue) { - EditForm.AddValidator((FieldIdentifier.Value.Model.GetType(), FieldIdentifier.Value.FieldName), this); + EditForm.AddValidator(FieldIdentifier.Value, this); } // 显式设置显示标签时一定显示 diff --git a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs index fd83d483b..794b8633a 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -20,8 +19,6 @@ namespace BootstrapBlazor.Components /// internal static class BootstrapBlazorEditContextDataAnnotationsExtensions { - private static readonly ConcurrentDictionary<(Type ModelType, string FieldName), Func> PropertyValueInvokerCache = new(); - /// /// 添加数据合规检查 /// @@ -51,12 +48,9 @@ namespace BootstrapBlazor.Components { var validationContext = new ValidationContext(editContext.Model); var validationResults = new List(); - - TryValidateObject(editContext.Model, validationContext, validationResults, editForm); - editForm.ValidateObject(editContext.Model, validationContext, validationResults); + editForm.ValidateObject(validationContext, validationResults); messages.Clear(); - foreach (var validationResult in validationResults.Where(v => !string.IsNullOrEmpty(v.ErrorMessage))) { if (!validationResult.MemberNames.Any()) @@ -84,99 +78,13 @@ namespace BootstrapBlazor.Components DisplayName = fieldIdentifier.GetDisplayName() }; - var propertyValue = fieldIdentifier.GetPropertyValue(); - TryValidateProperty(propertyValue, validationContext, results); - editForm.ValidateProperty(propertyValue, validationContext, results); + var propertyValue = LambdaExtensions.GetPropertyValue(fieldIdentifier.Model, fieldIdentifier.FieldName); + editForm.ValidateField(propertyValue, validationContext, results, fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName)!); messages.Clear(fieldIdentifier); messages.Add(fieldIdentifier, results.Where(v => !string.IsNullOrEmpty(v.ErrorMessage)).Select(result => result.ErrorMessage!)); editContext.NotifyValidationStateChanged(); } - - /// - /// 获取 FieldIdentifier 属性值 - /// - /// - /// - internal static object GetPropertyValue(this in FieldIdentifier fieldIdentifier) - { - var cacheKey = (fieldIdentifier.Model.GetType(), fieldIdentifier.FieldName); - var model = fieldIdentifier.Model; - var invoker = PropertyValueInvokerCache.GetOrAdd(cacheKey, key => LambdaExtensions.GetPropertyValueLambda(model, key.FieldName).Compile()); - - return invoker.Invoke(model); - } - - private static void TryValidateObject(object model, ValidationContext context, ICollection results, ValidateForm validateForm) - { - var modelType = model.GetType(); - var validateProperties = validateForm.ValidateAllProperties - ? modelType.GetRuntimeProperties().Where(p => p.IsPublic() && !p.GetIndexParameters().Any()) - : validateForm.GetPropertiesByModelType(model.GetType()).Select(p => model.GetType().GetProperty(p)); - foreach (var p in validateProperties) - { - if (p != null) - { - var fieldIdentifier = new FieldIdentifier(model, fieldName: p.Name); - var propertyValue = fieldIdentifier.GetPropertyValue(); - TryValidateProperty(propertyValue, context, results, p); - } - } - } - - private static void TryValidateProperty(object value, ValidationContext context, ICollection results, PropertyInfo? propertyInfo = null) - { - var modelType = context.ObjectType; - if (propertyInfo == null) - { - propertyInfo = modelType.GetProperty(context.MemberName!); - } - - if (propertyInfo != null) - { - var rules = propertyInfo.GetCustomAttributes(true).Where(i => i.GetType().BaseType == typeof(ValidationAttribute)).Cast(); - var displayName = new FieldIdentifier(context.ObjectInstance, propertyInfo.Name).GetDisplayName(); - var memberName = propertyInfo.Name; - var attributeSpan = "Attribute".AsSpan(); - foreach (var rule in rules) - { - if (!rule.IsValid(value)) - { - // 查找 resx 资源文件中的 ErrorMessage - var ruleNameSpan = rule.GetType().Name.AsSpan(); - var index = ruleNameSpan.IndexOf(attributeSpan, StringComparison.OrdinalIgnoreCase); - var ruleName = rule.GetType().Name.AsSpan().Slice(0, index); - var isResx = false; - if (!string.IsNullOrEmpty(rule.ErrorMessage)) - { - var resxType = ServiceProviderHelper.ServiceProvider.GetRequiredService>().Value.ResourceManagerStringLocalizerType; - if (resxType != null && JsonStringLocalizerFactory.TryGetLocalizerString(resxType, rule.ErrorMessage, out var resx)) - { - rule.ErrorMessage = resx; - isResx = true; - } - } - if (!isResx) - { - if (JsonStringLocalizerFactory.TryGetLocalizerString(rule.GetType(), nameof(rule.ErrorMessage), out var msg)) - { - rule.ErrorMessage = msg; - } - - if (JsonStringLocalizerFactory.TryGetLocalizerString(context.ObjectType, $"{memberName}.{ruleName.ToString()}", out msg)) - { - rule.ErrorMessage = msg; - } - } - - var errorMessage = rule.FormatErrorMessage(displayName ?? memberName); - results.Add(new ValidationResult(errorMessage, new string[] { memberName })); - } - } - } - } - - private static bool IsPublic(this PropertyInfo p) => p.GetMethod != null && p.SetMethod != null && p.GetMethod.IsPublic && p.SetMethod.IsPublic; } } diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index c69532895..a0af3068e 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -2,14 +2,18 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ +using BootstrapBlazor.Localization.Json; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Threading.Tasks; namespace BootstrapBlazor.Components @@ -64,66 +68,78 @@ namespace BootstrapBlazor.Components [Parameter] public RenderFragment? ChildContent { get; set; } + [Inject] + [NotNull] + private IServiceProvider? ServiceProvider { get; set; } + /// /// 验证组件缓存 /// - private ConcurrentDictionary<(Type ModelType, string FieldName), IValidateComponent> ValidatorCache { get; } = new(); + private ConcurrentDictionary ValidatorCache { get; } = new(); /// /// 添加数据验证组件到 EditForm 中 /// /// /// - internal void AddValidator((Type ModelType, string FieldName) key, IValidateComponent comp) => ValidatorCache.AddOrUpdate(key, k => comp, (k, c) => c = comp); + internal void AddValidator(in FieldIdentifier key, IValidateComponent comp) => ValidatorCache.AddOrUpdate(key, k => comp, (k, c) => c = comp); /// /// 设置指定字段错误信息 /// - /// 数据类型 /// 字段名称 /// 错误描述信息,可为空,为空时查找资源文件 - public void SetError(Type modelType, string filedName, string errorMessage) + public void SetError(string filedName, string errorMessage) { - if (ValidatorCache.TryGetValue((modelType, filedName), out var validator)) - { - var results = new List - { - new ValidationResult(errorMessage, new string[] { filedName }) - }; - validator.ToggleMessage(results, true); - } + //if (ValidatorCache.TryGetValue((modelType, filedName), out var validator)) + //{ + // var results = new List + // { + // new ValidationResult(errorMessage, new string[] { filedName }) + // }; + // validator.ToggleMessage(results, true); + //} } /// /// 通过指定的模型类型获取当前表单中的绑定属性 /// - /// /// - internal IEnumerable GetPropertiesByModelType(Type modelType) => ValidatorCache.Keys.Where(k => k.ModelType == modelType).Select(k => k.FieldName); + internal IEnumerable<(FieldIdentifier, PropertyInfo PropertyInfo)> GetBindModelProperties() => ValidatorCache.Keys.Select(key => (key, key.Model.GetType().GetProperty(key.FieldName)!)); /// /// EditModel 数据模型验证方法 /// - /// /// /// - internal void ValidateObject(object model, ValidationContext context, List results) + internal void ValidateObject(ValidationContext context, List results) { - // 遍历所有可验证组件进行数据验证 - foreach (var key in ValidatorCache) + if (ValidateAllProperties) { - if (key.Key.ModelType == context.ObjectType) + Validator.TryValidateProperty(context.ObjectInstance, context, results); + } + else + { + // 遍历所有可验证组件进行数据验证 + foreach (var key in ValidatorCache.Keys) { - var fi = new FieldIdentifier(model, key.Key.FieldName); - // 设置其关联属性字段 - var propertyValue = fi.GetPropertyValue(); - var validator = ValidatorCache[key.Key]; + var propertyValue = LambdaExtensions.GetPropertyValue(key.Model, key.FieldName); - // 数据验证 - context.MemberName = fi.FieldName; - validator.ValidateProperty(propertyValue, context, results); - validator.ToggleMessage(results, false); + // 验证 DataAnnotations + var messages = new List(); + ValidateDataAnnotations(propertyValue, context, messages, key.Model.GetType().GetProperty(key.FieldName)!); + + var validator = ValidatorCache[key]; + if (messages.Count == 0) + { + // 自定义验证组件 + validator.ValidateProperty(propertyValue, context, messages); + } + + // 客户端提示 + validator.ToggleMessage(messages, false); + results.AddRange(messages); } } } @@ -134,13 +150,66 @@ namespace BootstrapBlazor.Components /// /// /// - internal void ValidateProperty(object? propertyValue, ValidationContext context, List results) + /// + internal void ValidateField(object? propertyValue, ValidationContext context, List results, PropertyInfo propertyInfo) { - if (!string.IsNullOrEmpty(context.MemberName) && ValidatorCache.TryGetValue((context.ObjectType, context.MemberName), out var validator)) + if (ValidatorCache.TryGetValue(new FieldIdentifier(context.ObjectInstance, context.MemberName!), out var validator)) { - validator.ValidateProperty(propertyValue, context, results); + // 验证 DataAnnotations + ValidateDataAnnotations(propertyValue, context, results, propertyInfo); + + if (results.Count == 0) + { + // 自定义验证组件 + validator.ValidateProperty(propertyValue, context, results); + } + + // 客户端提示 validator.ToggleMessage(results, true); } } + + private void ValidateDataAnnotations(object? value, ValidationContext context, ICollection results, PropertyInfo propertyInfo) + { + var rules = propertyInfo.GetCustomAttributes(true).Where(i => i.GetType().BaseType == typeof(ValidationAttribute)).Cast(); + var displayName = new FieldIdentifier(context.ObjectInstance, propertyInfo.Name).GetDisplayName(); + var memberName = propertyInfo.Name; + var attributeSpan = "Attribute".AsSpan(); + foreach (var rule in rules) + { + if (!rule.IsValid(value)) + { + // 查找 resx 资源文件中的 ErrorMessage + var ruleNameSpan = rule.GetType().Name.AsSpan(); + var index = ruleNameSpan.IndexOf(attributeSpan, StringComparison.OrdinalIgnoreCase); + var ruleName = rule.GetType().Name.AsSpan().Slice(0, index); + var isResx = false; + if (!string.IsNullOrEmpty(rule.ErrorMessage)) + { + var resxType = ServiceProvider.GetRequiredService>().Value.ResourceManagerStringLocalizerType; + if (resxType != null && JsonStringLocalizerFactory.TryGetLocalizerString(resxType, rule.ErrorMessage, out var resx)) + { + rule.ErrorMessage = resx; + isResx = true; + } + } + if (!isResx) + { + if (JsonStringLocalizerFactory.TryGetLocalizerString(rule.GetType(), nameof(rule.ErrorMessage), out var msg)) + { + rule.ErrorMessage = msg; + } + + if (JsonStringLocalizerFactory.TryGetLocalizerString(context.ObjectType, $"{memberName}.{ruleName.ToString()}", out msg)) + { + rule.ErrorMessage = msg; + } + } + + var errorMessage = rule.FormatErrorMessage(displayName ?? memberName); + results.Add(new ValidationResult(errorMessage, new string[] { memberName })); + } + } + } } } -- Gitee From 30bf24c83f1a5c88e44488278f05ab42f7241679 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 01:19:52 +0800 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20ValidateForm=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E6=89=80=E6=9C=89=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ValidateForm/ValidateForm.razor.cs | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index a0af3068e..eaceb98ca 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -107,6 +107,8 @@ namespace BootstrapBlazor.Components /// internal IEnumerable<(FieldIdentifier, PropertyInfo PropertyInfo)> GetBindModelProperties() => ValidatorCache.Keys.Select(key => (key, key.Model.GetType().GetProperty(key.FieldName)!)); + private static bool IsPublic(PropertyInfo p) => p.GetMethod != null && p.SetMethod != null && p.GetMethod.IsPublic && p.SetMethod.IsPublic; + /// /// EditModel 数据模型验证方法 /// @@ -116,7 +118,7 @@ namespace BootstrapBlazor.Components { if (ValidateAllProperties) { - Validator.TryValidateProperty(context.ObjectInstance, context, results); + ValidateProperty(context, results); } else { @@ -145,7 +147,7 @@ namespace BootstrapBlazor.Components } /// - /// 字段验证方法 + /// 通过表单内绑定的字段验证方法 /// /// /// @@ -169,6 +171,13 @@ namespace BootstrapBlazor.Components } } + /// + /// 通过属性设置的 DataAnnotation 进行数据验证 + /// + /// + /// + /// + /// private void ValidateDataAnnotations(object? value, ValidationContext context, ICollection results, PropertyInfo propertyInfo) { var rules = propertyInfo.GetCustomAttributes(true).Where(i => i.GetType().BaseType == typeof(ValidationAttribute)).Cast(); @@ -211,5 +220,45 @@ namespace BootstrapBlazor.Components } } } + + /// + /// 验证整个模型时验证属性方法 + /// + /// + /// + private void ValidateProperty(ValidationContext context, List results) + { + var properties = context.ObjectType.GetRuntimeProperties().Where(p => IsPublic(p) && !p.GetIndexParameters().Any()); + foreach (var pi in properties) + { + // 设置其关联属性字段 + var propertyValue = LambdaExtensions.GetPropertyValue(context.ObjectInstance, pi.Name); + + // 检查当前值是否为 Class + if (propertyValue != null && propertyValue is not string && propertyValue.GetType().IsClass) + { + ValidateProperty(new ValidationContext(propertyValue), results); + } + else + { + // 验证 DataAnnotations + var messages = new List(); + ValidateDataAnnotations(propertyValue, context, messages, pi); + + if (ValidatorCache.TryGetValue(new FieldIdentifier(context.ObjectInstance, pi.Name), out var validator)) + { + if (messages.Count == 0) + { + // 自定义验证组件 + validator.ValidateProperty(propertyValue, context, messages); + } + + // 客户端提示 + validator.ToggleMessage(messages, true); + } + results.AddRange(messages); + } + } + } } } -- Gitee From 61c63b1e75696a266a5f524586696a5b8db23f26 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 01:31:02 +0800 Subject: [PATCH 06/12] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor.Shared/Locales/en-US.json | 4 ++++ src/BootstrapBlazor.Shared/Locales/zh-CN.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/BootstrapBlazor.Shared/Locales/en-US.json b/src/BootstrapBlazor.Shared/Locales/en-US.json index 0d4f113c9..bcf994a91 100644 --- a/src/BootstrapBlazor.Shared/Locales/en-US.json +++ b/src/BootstrapBlazor.Shared/Locales/en-US.json @@ -88,6 +88,10 @@ "Foo.Address2": "Earth, China, Lane {0} of Jinshajiang Road, Putuo District, Shanghai. Here is an example of super long cell", "Foo.BindValue": "BindValue" }, + "BootstrapBlazor.Shared.Pages.ValidateForms.Dummy2": { + "Name": "Name", + "Name.Required": "Dummy2 {0} is required" + }, "BootstrapBlazor.Shared.Pages.Components.EnumEducation": { "PlaceHolder": "Click to select ...", "Primary": "Primary", diff --git a/src/BootstrapBlazor.Shared/Locales/zh-CN.json b/src/BootstrapBlazor.Shared/Locales/zh-CN.json index 206bfbffb..72222d195 100644 --- a/src/BootstrapBlazor.Shared/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Shared/Locales/zh-CN.json @@ -88,6 +88,10 @@ "Foo.Address2": "地球、中国、上海市普陀区金沙江路 {0} 弄 这里是超长单元格示例", "Foo.BindValue": "绑定值" }, + "BootstrapBlazor.Shared.Pages.ValidateForms.Dummy2": { + "Name": "姓名", + "Name.Required": "Dummy2 {0} 值是必填项" + }, "BootstrapBlazor.Shared.Pages.Components.EnumEducation": { "PlaceHolder": "请选择 ...", "Primary": "小学", -- Gitee From a68478870439ea3956242df88d2f1c549c9c1296 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 01:33:21 +0800 Subject: [PATCH 07/12] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E5=A4=8D?= =?UTF-8?q?=E6=9D=82=E7=B1=BB=E5=9E=8B=E6=95=B0=E6=8D=AE=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/Samples/ValidateForms.razor | 18 ++++++++ .../Pages/Samples/ValidateForms.razor.cs | 43 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor index 06fd0e507..5de77ef3c 100644 --- a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor +++ b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor @@ -127,6 +127,24 @@ + +

本示例中绑定的是一个超级复杂类型 ComplexModel.Dummy.Dummy2.Name 清空值后,点击 提交表单 会对数据进行验证

+ +
+
+ +
+
+ +
+
+
+
+
+ +
+ diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs index 8650cfc13..5c659d867 100644 --- a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs +++ b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.Localization; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; @@ -28,6 +29,9 @@ namespace BootstrapBlazor.Shared.Pages [NotNull] private Logger? Trace3 { get; set; } + [NotNull] + private Logger? Trace4 { get; set; } + [Inject] [NotNull] private IStringLocalizer? Localizer { get; set; } @@ -46,6 +50,9 @@ namespace BootstrapBlazor.Shared.Pages [NotNull] private ValidateForm? Test { get; set; } + [NotNull] + private ComplexFoo? ComplexModel { get; set; } + /// baise /// OnInitialized 方法 /// @@ -56,6 +63,10 @@ namespace BootstrapBlazor.Shared.Pages // 初始化参数 Educations = typeof(EnumEducation).ToSelectList(new SelectedItem("", Localizer["PlaceHolder"] ?? "请选择 ...")); Hobbys = Foo.GenerateHobbys(LocalizerFoo); + ComplexModel = new ComplexFoo() + { + Dummy = new Dummy1() { Dummy2 = new Dummy2() }, + }; } private Task OnInvalidSubmit1(EditContext context) @@ -72,7 +83,7 @@ namespace BootstrapBlazor.Shared.Pages private Task OnValidateNameSubmit(EditContext context) { - Test.SetError(typeof(Foo), "Name", "数据库中已存在"); + //Test.SetError(typeof(Foo), "Name", "数据库中已存在"); return Task.CompletedTask; } @@ -88,6 +99,36 @@ namespace BootstrapBlazor.Shared.Pages return Task.CompletedTask; } + private Task OnInvalidComplexModel(EditContext context) + { + Trace4.Log("OnInvalidSubmit 回调委托"); + return Task.CompletedTask; + } + + private Task OnValidComplexModel(EditContext context) + { + Trace4.Log("OnValidSubmit 回调委托"); + return Task.CompletedTask; + } + + private class ComplexFoo : Foo + { + [NotNull] + public Dummy1? Dummy { get; set; } + } + + private class Dummy1 + { + [NotNull] + public Dummy2? Dummy2 { get; set; } + } + + private class Dummy2 + { + [Required] + public string? Name { get; set; } + } + #region 参数说明 private static IEnumerable GetAttributes() => new AttributeItem[] { -- Gitee From 7908a45b701b92cc379ffbedfc4e5901eda9eb20 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 10:47:10 +0800 Subject: [PATCH 08/12] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor.Shared/Locales/en-US.json | 4 ++++ src/BootstrapBlazor.Shared/Locales/zh-CN.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/BootstrapBlazor.Shared/Locales/en-US.json b/src/BootstrapBlazor.Shared/Locales/en-US.json index bcf994a91..c1090bbe7 100644 --- a/src/BootstrapBlazor.Shared/Locales/en-US.json +++ b/src/BootstrapBlazor.Shared/Locales/en-US.json @@ -88,6 +88,10 @@ "Foo.Address2": "Earth, China, Lane {0} of Jinshajiang Road, Putuo District, Shanghai. Here is an example of super long cell", "Foo.BindValue": "BindValue" }, + "BootstrapBlazor.Shared.Pages.ValidateForms.ComplexFoo": { + "Name": "Name", + "Name.Required": "{0} is required." + }, "BootstrapBlazor.Shared.Pages.ValidateForms.Dummy2": { "Name": "Name", "Name.Required": "Dummy2 {0} is required" diff --git a/src/BootstrapBlazor.Shared/Locales/zh-CN.json b/src/BootstrapBlazor.Shared/Locales/zh-CN.json index 72222d195..571c3e377 100644 --- a/src/BootstrapBlazor.Shared/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Shared/Locales/zh-CN.json @@ -88,6 +88,10 @@ "Foo.Address2": "地球、中国、上海市普陀区金沙江路 {0} 弄 这里是超长单元格示例", "Foo.BindValue": "绑定值" }, + "BootstrapBlazor.Shared.Pages.ValidateForms.ComplexFoo": { + "Name": "姓名", + "Name.Required": "{0} 值是必填项" + }, "BootstrapBlazor.Shared.Pages.ValidateForms.Dummy2": { "Name": "姓名", "Name.Required": "Dummy2 {0} 值是必填项" -- Gitee From 3e2f746593c6bdba9c533c6a8f1d263a4d9f9652 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 10:48:12 +0800 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20ValidateCont?= =?UTF-8?q?ext=20DisplayName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Validate/ValidateBase.cs | 7 --- ...zorEditContextDataAnnotationsExtensions.cs | 9 +-- .../ValidateForm/ValidateForm.razor.cs | 55 ++++++++++++------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs index 1f5633e9d..4e1c90777 100644 --- a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs +++ b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs @@ -408,13 +408,6 @@ namespace BootstrapBlazor.Components // 如果禁用移除验证信息 if (!IsDisabled && !SkipValidate) { - // 模型验证设置 验证属性名称 - // 验证组件内部使用 - if (string.IsNullOrEmpty(context.MemberName) && FieldIdentifier.HasValue) - { - context.MemberName = FieldIdentifier.Value.FieldName; - } - // 增加数值类型验证如 泛型 TValue 为 int 输入为 Empty 时 ValidateType(context, results); diff --git a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs index 794b8633a..a953e67c1 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/BootstrapBlazorEditContextDataAnnotationsExtensions.cs @@ -2,20 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ -using BootstrapBlazor.Localization.Json; using Microsoft.AspNetCore.Components.Forms; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Reflection; namespace BootstrapBlazor.Components { /// - /// EditContextDataAnotation 扩展操作类 + /// BootstrapBlazorEditContextDataAnnotationsExtensions 扩展操作类 /// internal static class BootstrapBlazorEditContextDataAnnotationsExtensions { @@ -78,8 +74,7 @@ namespace BootstrapBlazor.Components DisplayName = fieldIdentifier.GetDisplayName() }; - var propertyValue = LambdaExtensions.GetPropertyValue(fieldIdentifier.Model, fieldIdentifier.FieldName); - editForm.ValidateField(propertyValue, validationContext, results, fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName)!); + editForm.ValidateField(validationContext, results, fieldIdentifier); messages.Clear(fieldIdentifier); messages.Add(fieldIdentifier, results.Where(v => !string.IsNullOrEmpty(v.ErrorMessage)).Select(result => result.ErrorMessage!)); diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index eaceb98ca..c27feb490 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -129,19 +129,28 @@ namespace BootstrapBlazor.Components var propertyValue = LambdaExtensions.GetPropertyValue(key.Model, key.FieldName); // 验证 DataAnnotations - var messages = new List(); - ValidateDataAnnotations(propertyValue, context, messages, key.Model.GetType().GetProperty(key.FieldName)!); - - var validator = ValidatorCache[key]; - if (messages.Count == 0) + var pi = key.Model.GetType().GetProperty(key.FieldName); + if (pi != null) { - // 自定义验证组件 - validator.ValidateProperty(propertyValue, context, messages); - } + var validator = ValidatorCache[key]; + var messages = new List(); + var propertyValidateContext = new ValidationContext(key.Model) + { + MemberName = key.FieldName, + DisplayName = key.GetDisplayName() + }; + ValidateDataAnnotations(propertyValue, propertyValidateContext, messages, pi); - // 客户端提示 - validator.ToggleMessage(messages, false); - results.AddRange(messages); + if (messages.Count == 0) + { + // 自定义验证组件 + validator.ValidateProperty(propertyValue, propertyValidateContext, messages); + } + + // 客户端提示 + validator.ToggleMessage(messages, false); + results.AddRange(messages); + } } } } @@ -149,16 +158,18 @@ namespace BootstrapBlazor.Components /// /// 通过表单内绑定的字段验证方法 /// - /// /// /// - /// - internal void ValidateField(object? propertyValue, ValidationContext context, List results, PropertyInfo propertyInfo) + /// + internal void ValidateField(ValidationContext context, List results, in FieldIdentifier fieldIdentifier) { - if (ValidatorCache.TryGetValue(new FieldIdentifier(context.ObjectInstance, context.MemberName!), out var validator)) + if (ValidatorCache.TryGetValue(fieldIdentifier, out var validator)) { + var propertyValue = LambdaExtensions.GetPropertyValue(fieldIdentifier.Model, fieldIdentifier.FieldName); + var pi = fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName)!; + // 验证 DataAnnotations - ValidateDataAnnotations(propertyValue, context, results, propertyInfo); + ValidateDataAnnotations(propertyValue, context, results, pi); if (results.Count == 0) { @@ -181,7 +192,7 @@ namespace BootstrapBlazor.Components private void ValidateDataAnnotations(object? value, ValidationContext context, ICollection results, PropertyInfo propertyInfo) { var rules = propertyInfo.GetCustomAttributes(true).Where(i => i.GetType().BaseType == typeof(ValidationAttribute)).Cast(); - var displayName = new FieldIdentifier(context.ObjectInstance, propertyInfo.Name).GetDisplayName(); + var displayName = context.DisplayName; var memberName = propertyInfo.Name; var attributeSpan = "Attribute".AsSpan(); foreach (var rule in rules) @@ -237,15 +248,21 @@ namespace BootstrapBlazor.Components // 检查当前值是否为 Class if (propertyValue != null && propertyValue is not string && propertyValue.GetType().IsClass) { - ValidateProperty(new ValidationContext(propertyValue), results); + var fieldContext = new ValidationContext(propertyValue) + { + MemberName = pi.Name + }; + ValidateProperty(fieldContext, results); } else { // 验证 DataAnnotations + var fieldIdentifier = new FieldIdentifier(context.ObjectInstance, pi.Name); var messages = new List(); + context.DisplayName = fieldIdentifier.GetDisplayName(); ValidateDataAnnotations(propertyValue, context, messages, pi); - if (ValidatorCache.TryGetValue(new FieldIdentifier(context.ObjectInstance, pi.Name), out var validator)) + if (ValidatorCache.TryGetValue(fieldIdentifier, out var validator)) { if (messages.Count == 0) { -- Gitee From 0939b874f2d45719a01092323cedfa9101ed448f Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 11:25:02 +0800 Subject: [PATCH 10/12] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20SetError=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ValidateForm/ValidateForm.razor.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index c27feb490..2e3a2e880 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; @@ -87,18 +88,27 @@ namespace BootstrapBlazor.Components /// /// 设置指定字段错误信息 /// - /// 字段名称 + /// /// 错误描述信息,可为空,为空时查找资源文件 - public void SetError(string filedName, string errorMessage) + public void SetError(Expression> expression, string errorMessage) { - //if (ValidatorCache.TryGetValue((modelType, filedName), out var validator)) - //{ - // var results = new List - // { - // new ValidationResult(errorMessage, new string[] { filedName }) - // }; - // validator.ToggleMessage(results, true); - //} + if (expression.Body is MemberExpression exp) + { + var fieldName = exp.Member.Name; + var modelType = exp.Expression?.Type; + if (modelType != null) + { + var validator = ValidatorCache.FirstOrDefault(c => c.Key.Model.GetType() == modelType && c.Key.FieldName == fieldName).Value; + if (validator != null) + { + var results = new List + { + new ValidationResult(errorMessage, new string[] { fieldName }) + }; + validator.ToggleMessage(results, true); + } + } + } } /// -- Gitee From 67e43c039f384a5365d0306924d2fbd5440a3643 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 11:25:15 +0800 Subject: [PATCH 11/12] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20SetError=20?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pages/Samples/ValidateForms.razor | 4 ++-- .../Pages/Samples/ValidateForms.razor.cs | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor index 5de77ef3c..7c87c1a37 100644 --- a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor +++ b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor @@ -76,7 +76,7 @@ 应用场景
客户端验证通过后进行数据库保存操作,如果出现其他问题,后仍然可以进行表单自定义错误提示,本例中数据验证合法后,点击 提交表单 按钮后,姓名字段会显示 数据库中已存在 这样的自定义提示信息

- +
@@ -129,7 +129,7 @@

本示例中绑定的是一个超级复杂类型 ComplexModel.Dummy.Dummy2.Name 清空值后,点击 提交表单 会对数据进行验证

- +
diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs index 5c659d867..673fff40b 100644 --- a/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs +++ b/src/BootstrapBlazor.Shared/Pages/Samples/ValidateForms.razor.cs @@ -81,12 +81,6 @@ namespace BootstrapBlazor.Shared.Pages return Task.CompletedTask; } - private Task OnValidateNameSubmit(EditContext context) - { - //Test.SetError(typeof(Foo), "Name", "数据库中已存在"); - return Task.CompletedTask; - } - private Task OnInvalidSubmit(EditContext context) { Trace2.Log("OnInvalidSubmit 回调委托"); @@ -108,6 +102,7 @@ namespace BootstrapBlazor.Shared.Pages private Task OnValidComplexModel(EditContext context) { Trace4.Log("OnValidSubmit 回调委托"); + Test.SetError(f => f.Dummy.Dummy2.Name, "数据库中已存在"); return Task.CompletedTask; } -- Gitee From c17f3c5f7fe3898f3392a9f52169d9daf38a8a38 Mon Sep 17 00:00:00 2001 From: Argo-Cloud Date: Sun, 21 Mar 2021 11:27:56 +0800 Subject: [PATCH 12/12] release: publish 5.0.22-beta02 --- src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index cae1d8c5c..708fe8880 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -4,7 +4,7 @@ net5.0 true logo.png - 5.0.22-beta01 + 5.0.22-beta02 https://gitee.com/LongbowEnterprise/BootstrapBlazor/wikis -- Gitee