简体   繁体   English

自定义类的 Blazor 验证

[英]Blazor validation for custom class

I'm testing out Blazor and I've run into a validation issue.我正在测试 Blazor,但遇到了验证问题。 When validating a simple class I can just use annotations.在验证一个简单的类时,我可以只使用注释。 If I have my own custom class inside though validation doesn't run for everything inside my custom class.如果我有自己的自定义类,尽管验证不会对我的自定义类中的所有内容运行。 The issue seems to be specific to Blazor since I can use this validation in ASP.该问题似乎特定于 Blazor,因为我可以在 ASP 中使用此验证。

Here are my two simple models:这是我的两个简单模型:

public class TestModel
{
    [Required]
    [Range(12, 400, ErrorMessage = "This works")]
    public int Count { get; set; }

    public KeyValue KeyValues { get; set; }
    public TestModel()
    {
        Count = 4;
        KeyValues = new KeyValue()
        {
            Key = 5,
            Value = "str"
        };
    }
}

And the KeyValue class和 KeyValue 类

public class KeyValue
{
    [Required]
    [Range(10, 300, ErrorMessage = "This number check doesn't")]
    public int Key { get; set; }
    [Required]
    [StringLength(10, MinimumLength = 5, ErrorMessage = "Nor the string one")]
    public string Value { get; set; }
}

And that is my component.这就是我的组成部分。 It validates the Model.Count property, but doesn't validate the nested class.它验证 Model.Count 属性,但不验证嵌套类。

<EditForm Model="@Model" OnValidSubmit="@DoStuff">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div class="row">
        <div class="col-md-4">
            <input type="number" bind="@Model.Count" class="form-control" placeholder="Condition property name" />
        </div>
        <div class="col-md-4">
            <input type="number" bind="@Model.KeyValues.Key" class="form-control" placeholder="Condition property name" />
        </div>
        <div class="col-md-4">
            <InputText bind-Value="@Model.KeyValues.Value"></InputText>
        </div>

    </div>
    <div class="row">
        <div class="col-md-12">
            <button type="submit" class="btn btn-info">Create</button>
        </div>
    </div>
</EditForm>

This is a known limitation of Blazor, but you can work around it.这是 Blazor 的一个已知限制,但您可以解决它。

First, use the OnSubmit event on <EditForm> instead of OnValidSubmit .首先,在<EditForm>上使用OnSubmit事件而不是OnValidSubmit The method is passed an EditContext like so...该方法被传递一个EditContext像这样......

private void FormSubmitted(EditContext context)
{
  ...
}

If you use the following extension you can use the following code in your FormSubmitted method and it will not only validate your entire object tree but also update your UI according to the result.如果您使用以下扩展,您可以在FormSubmitted方法中使用以下代码,它不仅会验证您的整个对象树,还会根据结果更新您的 UI。

{
  if (context.ValdiateObjectTree())
  {
    ... do whatever
  }
}

The extension...扩展...

using Microsoft.AspNetCore.Components.Forms;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace PeterLeslieMorris.Blazor.Validation.Extensions
{
    public static class EditContextExtensions
    {
        static PropertyInfo IsModifiedProperty;
        static MethodInfo GetFieldStateMethod;

        /// <summary>
        /// Validates an entire object tree
        /// </summary>
        /// <param name="editContext">The EditContext to validate the Model of</param>
        /// <returns>True if valid, otherwise false</returns>
        public static bool ValidateObjectTree(this EditContext editContext)
        {
            var validatedObjects = new HashSet<object>();
            ValidateObject(editContext, editContext.Model, validatedObjects);
            editContext.NotifyValidationStateChanged();
            return !editContext.GetValidationMessages().Any();
        }

        public static void ValidateProperty(this EditContext editContext, FieldIdentifier fieldIdentifier)
        {
            if (fieldIdentifier.Model == null)
                return;

            var propertyInfo = fieldIdentifier.Model.GetType().GetProperty(
                fieldIdentifier.FieldName,
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);

            var validatedObjects = new HashSet<object>();
            ValidateProperty(editContext, fieldIdentifier.Model, propertyInfo, validatedObjects);
        }

        private static void ValidateObject(
            EditContext editContext,
            object instance,
            HashSet<object> validatedObjects)
        {
            if (instance == null)
                return;

            if (validatedObjects.Contains(instance))
                return;

            if (instance is IEnumerable && !(instance is string))
            {
                foreach (object value in (IEnumerable)instance)
                    ValidateObject(editContext, value, validatedObjects);
                return;
            }

            if (instance.GetType().Assembly == typeof(string).Assembly)
                return;

            validatedObjects.Add(instance);

            var properties = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (PropertyInfo property in properties)
                ValidateProperty(editContext, instance, property, validatedObjects);
        }

        private static void ValidateProperty(
            EditContext editContext,
            object instance,
            PropertyInfo property,
            HashSet<object> validatedObjects)
        {
            NotifyPropertyChanged(editContext, instance, property.Name);

            object value = property.GetValue(instance);
            ValidateObject(editContext, value, validatedObjects);
        }

        private static void NotifyPropertyChanged(
            EditContext editContext,
            object instance,
            string propertyName)
        {
            if (GetFieldStateMethod == null)
            {
                GetFieldStateMethod = editContext.GetType().GetMethod(
                    "GetFieldState",
                    BindingFlags.NonPublic | BindingFlags.Instance);
            }

            var fieldIdentifier = new FieldIdentifier(instance, propertyName);
            object fieldState = GetFieldStateMethod.Invoke(editContext, new object[] { fieldIdentifier, true });

            if (IsModifiedProperty == null)
            {
                IsModifiedProperty = fieldState.GetType().GetProperty(
                    "IsModified",
                    BindingFlags.Public | BindingFlags.Instance);
            }

            object originalIsModified = IsModifiedProperty.GetValue(fieldState);
            editContext.NotifyFieldChanged(fieldIdentifier);
            IsModifiedProperty.SetValue(fieldState, originalIsModified);
        }

    }
}

You can find the extension source here .您可以在此处找到扩展源。 You could alternatively use Blazor-Validation , which also allows you to use FluentValidation .您也可以使用Blazor-Validation ,它也允许您使用FluentValidation

If you want a more in-depth understanding of how Blazor forms / validation works, you can read about it in this section of Blazor University .如果您想更深入地了解 Blazor 表单/验证的工作原理,可以在Blazor 大学的这一部分阅读相关内容。

In case someone else stumbles into this issue it isn't possible right now.如果其他人偶然发现这个问题,现在不可能。 It should be available in 3.0.0-preview8 according to this post https://github.com/aspnet/AspNetCore/issues/10896根据这篇文章,它应该在 3.0.0-preview8 中可用https://github.com/aspnet/AspNetCore/issues/10896

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM