Search code examples
asp.net-mvcvalidationdata-collection

Validating MVC Data Collections


I have a simple Question data model:

public class Question {
    int QuestionId { get; set; }
    string Text { get; set; }
    string Answer { get; set; }
    string ValidationMessage { get; set; }
};

Using this class I have built a view Model:

public class QuestionViewModel {
    string Introduction { get; set; }
    IEnumerable<Question> Questions { get; set; }
};

My Controller the builds the view model (from a data source) and renders the view:

@model QuestionViewModel

@using (Html.BeginForm()) {
    if (Model.Questions != null) {
        <ol>
            @Html.EditorFor(m => Model.Questions)
        </ol>
    }
    @Html.ValidationSummary("Unable to process answers...")

    <input type="submit" value="submit" />
}

This view utilises an EditorTemplate:

@model Question

<li>
    @Html.HiddenFor(m => m.Questionid)
    @Html.TextBoxFor(m => m.Answer)
    @Html.ValidationMessageFor(m => m.Answer)
</li>

For now, when the page is posted back, the controller validates the response:

[HttpPost]
public ActionResult Response(QuestionViewModel model) {
    if (ModelState.IsValid) {
       for (int i = 0; i < model.Questions.Count(); i++) {
           Question q = model.Questions[i];
           string questionId = String.Format("Questions[{0}]", i);

           if (String.IsNullOrWhiteSpace(q.Answer)) {
               ModelState.AddModelError(questionId, q.ValidationMessage);
           }
       }
    }
}

The problem I'm having is that most of this works fine - the validates and the Validation Summary shows the correct validation messages. The problem is that I can't get individual field validators to render the error:

<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="Questions[0].StringValue"></span>

As you can see, when I call the ModelState.AddModelError() method, I am currently using key value of the format "Questions[0]", but I have also tried "Questions_0" and various other combinations.

Any help/guidance would be much appreciated.

[Apologies for the overly long post]


Solution

  • I have found the answer - as with so many things, it was obvious once I broke the problem down - the ModelState.AddModelError() just needed a fully qualified key!

    Modify the HttpPost Controller as follows:

    [HttpPost]
    public ActionResult Response(QuestionViewModel model) {
        if (ModelState.IsValid) {
           for (int i = 0; i < model.Questions.Count(); i++) {
               Question q = model.Questions[i];
    
               /*
               ** The key must specify a fully qualified element name including
               ** the name of the property value, e.g.
               ** "Questions[0].Answer"
               */
               string questionId = String.Format("Questions[{0}].Answer", i);
    
               if (String.IsNullOrWhiteSpace(q.Answer)) {
                   ModelState.AddModelError(questionId, q.ValidationMessage);
               }
           }
        }
    }