Reputation: 8474
There is a complex view model that needs to be validated on the client. Simplified:
public class Survey
{
public List<Question> Questions { get; set; }
}
public class Question
{
public List<Answer> Answers { get; set; }
}
public class Answer
{
public string FieldName { get; set; }
[Required("Please use a number")]
public int Number { get; set; }
}
Currently, the question is being validated correctly on the client. But we need to validate and display a contextual message using FieldName like:
The field 'Number of children' is required.
I implemented a CustomRequiredAttribute
class (: RequiredAttribute, IClientValidatable
) and decorated the Number
property with it:
[CustomRequired("{0} is mandatory")]
public int Number { get; set; }
but, in the GetClientValidationRules
method, the metadata.Model is null.
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata,
ControllerContext context)
{
// metadata.Model is null here!
ModelMetadata answerFieldNameValue = ModelMetadataProviders.Current
.GetMetadataForProperties(
metadata.Model,
metadata.ContainerType)
.FirstOrDefault(p => p.PropertyName == "FieldName");
// and, therefore, answerFieldNameValue.Model is null too!
}
If I go back to the first line of this method and from the context.Controller.ViewData.Model
I get any Answer
and assign it to metadata.Model
, then the answerFieldNameValue.Model
will contain the proper string which is the field name of the answer.
How to make this metadata.Model have a proper value when it gets here? Or any other way to solve this problem?
Upvotes: 2
Views: 2177
Reputation: 8474
Got it. The code below is my solution which was inspired by BranTheMan who found his way out of a similar problem.
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private object lastQuestionLabel;
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
ModelMetadata modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
object model = null;
if (modelAccessor != null)
{
model = modelAccessor();
}
if (typeof(SurveyQuestionVM).IsAssignableFrom(containerType) && propertyName == "TreeViewLabel")
{
lastQuestionLabel = model;
}
if (typeof(SurveyAnswerVM).IsAssignableFrom(containerType))
{
modelMetadata.AdditionalValues.Add("QuestionLabel", lastQuestionLabel);
}
return modelMetadata;
}
}
Upvotes: 1