0

In my MVC 5 application I have multiple tables based on a grouping. In the tables it is possible to change some values. When the FirstName field is empty. When I submit the form, client-side validation is not working and I don't know why.

Model:

public class ResourceModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }
}

Controller:

public ActionResult Employee()
{
    List<ResourceModel> resources = new List<ResourceModel>();

    resources.Add(new ResourceModel() { Id = 1, FirstName = "Piet", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 2, FirstName = "Jan", LastName = "Janssen" });
    resources.Add(new ResourceModel() { Id = 3, FirstName = "Willem", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 4, FirstName = "Hendrik", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 5, FirstName = "James", LastName = "Janssen" });

    return View(resources.ToList());
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Validation(List<ResourceModel> resource)
{
    if(ModelState.IsValid != true)
    {
        return RedirectToAction("Employee");
    }
    return RedirectToAction("Employee");
}

View:

@model List<EmployeeApp.Models.ResourceModel>

@{
    ViewBag.Title = "Employee";
}

<h2>Employee</h2>
@using (Html.BeginForm("Validation", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    int i = 0;
    foreach (var grp in Model
    .OrderBy(m => m.LastName)
    .GroupBy(m => new { m.LastName }))
    {
        <table>
            <thead>
                <tr>
                    <th>First name</th>
                    <th>Last name</th>
                </tr>
            </thead>
            <tbody>

                    @foreach (var itm in grp)
                    {
                        <tr>
                            <td>
                                <input type="hidden" value="@i" name="Index" />
                                @Html.TextBoxFor(m => itm.FirstName, new { Name = "[" + i + "].FirstName", @id = "[" + i + "].FirstName" })
                            </td>
                            <td>
                                @Html.TextBox("[" + i + "].LastName", itm.LastName)
                                @Html.ValidationMessage("[" + i + "].LastName")
                            </td>
                        </tr>

                        i++;
                    }

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

When I change my view to:

<table>
<thead><tr><td>First name</td><td>Last name</td></tr><thead>
<tbody>
for(int i = 0; i < Model.Count(); i++)
{
    <tr>
        <td>@Html.TextBoxFor(m => m[i].FirstName)</td>
        <td>@Html.TextBoxFor(m => m[i].LastName)
            @Html.ValidationMessageFor(m => m[i].LastName)</td>
    </tr>
}
</tbody>
</table>

Client-side validation is working.

kwv84
  • 943
  • 3
  • 11
  • 25
  • 1
    Show you script for adding and deleting rows (the code you have shown should work fine for validating existing items) –  Nov 30 '15 at 22:28
  • @StephenMuecke Also without adding/removing a row, client-side validation is working when I'm not using the index input and not working when using the index input. So I don't know if my jquery has something to do with it. – kwv84 Nov 30 '15 at 22:45
  • You have not shown how you set the variable `rowCount` but assuming its based on a the number of rows in the table, then it will not work (your can end up with duplicate indexers. Also, you need to remove the script where you add rules and instead re-parse the jquery validator –  Nov 30 '15 at 22:52
  • Also you not even adding the validation message placeholder (i.e. as generated by `@ValidationMessageFor()`) for the new items you adding. Suggest you look at [this answer](http://stackoverflow.com/questions/29837547/set-class-validation-for-dynamic-textbox-in-a-table/29838689#29838689) –  Nov 30 '15 at 23:50
  • @StephenMuecke thnx for your answer. I've changed the question, because I was not complete. Although I have to change my jquery to add/remove rows, based on your provided link, I think my problem is in the grouping of the list and doing a foreach loop. – kwv84 Dec 01 '15 at 10:36
  • Yes, a `foreach` loop will not give correct 2-way model binding (and therefore validation) And what is the point of the `.Group()`? - your not using the key anywhere in the view. And you `.OrderBy()` should be done in the controller before you pass the model to the view. –  Dec 01 '15 at 11:08
  • @StephenMuecke The next step is that I want a table for every LastName and then, before every table starts, I have something like: `

    @grp.Key.LastName

    `
    – kwv84 Dec 01 '15 at 11:28

1 Answers1

0

Based on the comments of this question, I've found a solution that is working for me.

I've put the .OrderBy in the controller and changed the view:

Controller

public ActionResult Employee()
{
    List<ResourceModel> resources = new List<ResourceModel>();

    resources.Add(new ResourceModel() { Id = 1, FirstName = "Piet", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 2, FirstName = "Jan", LastName = "Janssen" });
    resources.Add(new ResourceModel() { Id = 3, FirstName = "Willem", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 4, FirstName = "Hendrik", LastName = "Willemse" });
    resources.Add(new ResourceModel() { Id = 5, FirstName = "James", LastName = "Janssen" });

    var orderedResources = resources.OrderBy(r => r.LastName).Select(m => new ResourceModel()
    {
        Id = m.Id,
        LastName = m.LastName,
        FirstName = m.FirstName
    }).ToList();

    return View(orderedResources);
}

View

@model List<EmployeeApp.Models.ResourceModel>

@{
    ViewBag.Title = "Employee";
}

<h2>Employee</h2>
@using (Html.BeginForm("Validation", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    int x = 0;
    foreach(var grp in Model.GroupBy(m => new { m.LastName }))
    {
        <h3>@grp.Key.LastName</h3>
        <table>
            <thead>
                <tr>
                    <th>First name</th>
                    <th>Last name</th>
                </tr>
            </thead>
            <tbody>

                        @for(int i = 0; i < @Model.Where(m => m.LastName == grp.Key.LastName).Count(); i++)
                        {   
                        <tr>
                            <td>
                                <input type="hidden" value="@x" name="Index" />
                                @Html.TextBoxFor(r => r[x].FirstName)
                            </td>
                            <td>
                                @Html.TextBoxFor(r => r[x].LastName)
                                @Html.ValidationMessageFor(r => r[x].LastName)
                            </td>
                        </tr>
                            x++;

                        }


                <tr>
                    <td>
                        <input type="hidden" value="@x + 1" name="Index" />
                        <input type="text" name="[@x + 1].FirstName" />
                    </td>
                    <td>
                        <input data-val="true" data-val-required="The field is required" name="[@x + 1].LastName" type="text" value="">
                        <span class="field-validation-valid errorText" data-valmsg-for="[@x + 1].LastName" data-valmsg-replace="true"></span>
                    </td>
                </tr>

            </tbody>
        </table>
    }
    <input type="submit" value="submit" />
}
kwv84
  • 943
  • 3
  • 11
  • 25