In my ASP.NET MVC 5 (C#) application, I have a class called Name:
public class Name
{
public virtual string? First { get; set; }
public virtual string? Middle { get; set; }
public virtual string? Last { get; set; }
public virtual string? Suffix { get; set; }
}
I also have several models containing a property (of type name) called NameFull:
[RequiredProps(nameof(Name.First), nameof(Name.Last))] // *** More about this in a minute. ***
public Name? NameFull { get; set; }
These model properties get used by a custom tag helper like this: <field asp-for="NameFull" />
. There's logic inside the custom tag helper detects that the model property is a complex type and generates a custom HTML helper since HTML helpers can handle complex objects unlike tag helpers. So basically this code <field asp-for="NameFull" />
generates multiple input elements. That's the background, here's my problem...
I want to be able to make some of those inputs required on a case-by-case basis. So maybe in one model first and last name are required. In another model, none of them are required. So I created a custom attribute named RequiredPropsAttribute (see model example above). It inherits from ValidationAttribute and implements IClientModelValidator AttributeAdapter to apply client-side validation like so:
public sealed class RequiredPropsAttribute : ValidationAttribute, IClientModelValidator
{
//most of the logic has been omitted for brevity...
public void AddValidation(ClientModelValidationContext context)
{
var properties = context.ModelMetadata.Properties;
if (!context.Attributes.ContainsKey("data-val"))
{
context.Attributes.Add("data-val", "true");
}
if (!context.Attributes.ContainsKey("data-val-required"))
{
context.Attributes.Add("data-val-required", $"The {string.Join(", ", properties)} fields are all required.");
}
}
}
This does allow me to add validation to the parent property (Name), but what I want is to apply the validation to its nested children. In short, I want something similar to the above code that injects data-val="true"
and data-val-required="This field is required"
into the nested child inputs (Name.First, Name.Middle, Name.Last, Name.Suffix). I've tried accessing the children using context.ModelMetadata.Properties
, but the collection isn't set up in such a way to where I can write code like this: property.Attributes.Add("data-val", "true")
. Which leads me to my question...
QUESTION: How do I write logic inside AddValidation that lets me basically say: loop through the child properties of Name and add attributes to each of them so each child <input>
gets data-
validation attributes. Is this possible? And if not, is there any other way I can conditionally add a required attribute to the name properties based on attributes applied to name, e.g. add a RequiredIf to First
that says "if Name has a RequiredProps attribute containing First
, then make first required, etc.?
Thanks in advance for your help, everyone!