Created
August 26, 2019 13:25
-
-
Save SteveSandersonMS/86a0bd3be0826f57af1ae2d3bd5fa95c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@page "/" | |
<h1>Hello, world!</h1> | |
Welcome to your new app. | |
<EditForm Model="@_editingPerson" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit"> | |
<p> | |
Name: <InputText @bind-Value="_editingPerson.Name" /> | |
<ValidationMessage For="@(() => _editingPerson.Name)" /> | |
</p> | |
<p> | |
Address L1: <InputText @bind-Value="_editingPerson.HomeAddress.Line1" /> | |
<ValidationMessage For="@(() => _editingPerson.HomeAddress.Line1)" /> | |
</p> | |
<p> | |
Address city: <InputText @bind-Value="_editingPerson.HomeAddress.City" /> | |
<ValidationMessage For="@(() => _editingPerson.HomeAddress.City)" /> | |
</p> | |
<button type="submit">Submit</button> | |
<MyDataAnnotationsValidator /> | |
<ValidationSummary /> | |
</EditForm> | |
<pre>@_message</pre> | |
@code { | |
string _message = ""; | |
private Person _editingPerson = new Person(); | |
void OnValidSubmit() | |
{ | |
_message += "Valid submit\n"; | |
} | |
void OnInvalidSubmit() | |
{ | |
_message += "Invalid submit\n"; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyDataAnnotationsValidator : ComponentBase | |
{ | |
private static object _validationContextValidatorKey = new object(); | |
private ValidationMessageStore _messages; | |
[CascadingParameter] EditContext EditContext { get; set; } | |
protected override void OnInitialized() | |
{ | |
_messages = new ValidationMessageStore(EditContext); | |
// Perform object-level validation (starting from the root model) on request | |
EditContext.OnValidationRequested += (sender, eventArgs) => | |
{ | |
_messages.Clear(); | |
ValidateObject(EditContext.Model); | |
EditContext.NotifyValidationStateChanged(); | |
}; | |
// Perform per-field validation on each field edit | |
EditContext.OnFieldChanged += (sender, eventArgs) => | |
ValidateField(EditContext, _messages, eventArgs.FieldIdentifier); | |
} | |
private void ValidateObject(object value) | |
{ | |
var validationContext = new ValidationContext(value); | |
validationContext.Items.Add(_validationContextValidatorKey, this); | |
var validationResults = new List<ValidationResult>(); | |
Validator.TryValidateObject(value, validationContext, validationResults, true); | |
// Transfer results to the ValidationMessageStore | |
foreach (var validationResult in validationResults) | |
{ | |
foreach (var memberName in validationResult.MemberNames) | |
{ | |
var fieldIdentifier = new FieldIdentifier(value, memberName); | |
_messages.Add(fieldIdentifier, validationResult.ErrorMessage); | |
} | |
} | |
} | |
internal static void TryValidateRecursive(object value, ValidationContext validationContext) | |
{ | |
if (validationContext.Items.TryGetValue(_validationContextValidatorKey, out object validator)) | |
{ | |
((MyDataAnnotationsValidator)validator).ValidateObject(value); | |
} | |
} | |
private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier) | |
{ | |
// DataAnnotations only validates public properties, so that's all we'll look for | |
var propertyInfo = fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName); | |
if (propertyInfo != null) | |
{ | |
var propertyValue = propertyInfo.GetValue(fieldIdentifier.Model); | |
var validationContext = new ValidationContext(fieldIdentifier.Model) | |
{ | |
MemberName = propertyInfo.Name | |
}; | |
var results = new List<ValidationResult>(); | |
Validator.TryValidateProperty(propertyValue, validationContext, results); | |
messages.Clear(fieldIdentifier); | |
messages.Add(fieldIdentifier, results.Select(result => result.ErrorMessage)); | |
// We have to notify even if there were no messages before and are still no messages now, | |
// because the "state" that changed might be the completion of some async validation task | |
editContext.NotifyValidationStateChanged(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Person | |
{ | |
[Required] public string Name { get; set; } | |
[ValidateRecursive] public Address HomeAddress { get; } = new Address(); | |
} | |
public class Address | |
{ | |
[Required] public string Line1 { get; set; } | |
[Required] public string City { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ValidateRecursiveAttribute : ValidationAttribute | |
{ | |
protected override ValidationResult IsValid(object value, ValidationContext validationContext) | |
{ | |
MyDataAnnotationsValidator.TryValidateRecursive(value, validationContext); | |
return ValidationResult.Success; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment