Pascal
Pascal

Reputation: 12855

Validation message is not shown for an item with ModelState invalid

When I edit a device`s name to empty/null so the ModelState is invalid, how can I return the list of all devices BUT show the one edited device with a validation message that the name must be entered?

At the moment the validation error is only visible over the device table, but I want to show the validation message to the left of the TextBoxFor Helper.

Why is the validation message not visible there?

VIEW

@model IEnumerable<MVCTest.Models.DeviceViewModel>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>

@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<table class="table">
    <tr>
        <th>

        </th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
        using (Html.BeginForm("Save", "Device"))
        {
            <tr>
                <td style="background: alice;">
                    @Html.ValidationMessageFor(m => item.Name)
                </td>
                <td>
                    @Html.TextBoxFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                    @Html.ActionLink("Details", "Details", new { id = item.Id }) |
                    @Html.ActionLink("Delete", "Delete", new { id = item.Id })
                </td>
                <td>
                    <input type="submit" value="Save" />
                </td>
            </tr>
        }
    }
</table>

CONTROLLER

public class DeviceController : Controller
    {
        private List<DeviceViewModel> GetDevices()
        {
            return new List<DeviceViewModel>
            {
                new DeviceViewModel{ Id = 1, Name = "Test 1"},
                new DeviceViewModel{ Id = 1, Name = "Test 2"},
                new DeviceViewModel{ Id = 1, Name = "Test 3"},
            };
        }

        // GET: Device
        public ActionResult Index()
        {
            var devices = GetDevices();
            return View(devices);
        }

        public ActionResult Save(DeviceViewModel viewModel)
        {
            if (ModelState.IsValid)
            {
                // save to db
                return RedirectToAction("Index");
            }

            return View("Index", GetDevices());
        }
    }

ViewModel

  public class DeviceViewModel
    {
        public int Id { get; set; }

        [Required(ErrorMessage = "You must enter a name!")]
        public string Name { get; set; }

    }

Upvotes: 1

Views: 1069

Answers (2)

hutchonoid
hutchonoid

Reputation: 33305

The problem is this line:

@Html.ValidationMessageFor(m => item.Name,"")

It is using a blank error message as the specified message.

Change it to:

@Html.ValidationMessageFor(m => item.Name)

Or:

@Html.ValidationMessageFor(m => item.Name,"A custom error message.")

Update

You need to return your viewModel back to the view as well instead of creating a new one after the post.

public ActionResult Save(DeviceViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        // save to db
        return RedirectToAction("Index");
    }

    return View("Index", viewModel);
}

In order for your collection to be posted back to the server you need to use a for loop rather than a foreach.

See here: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

@foreach (var i = 0; i< Model.Count(); i++)
    {
        using (Html.BeginForm("Save", "Device"))
        {
            <tr>
                <td style="background: alice;">
                    @Html.ValidationMessageFor(m => Model[i].Name)
                    @Html.HiddenFor(m => m.Id)
                </td>
                <td>
                    @Html.TextBoxFor(modelItem =>  Model[i].Name)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { id =  Model[i].Id }) |
                    @Html.ActionLink("Details", "Details", new { id =  Model[i].Id }) |
                    @Html.ActionLink("Delete", "Delete", new { id =  Model[i].Id })
                </td>
                <td>
                    <input type="submit" value="Save" />
                </td>
            </tr>
        }
    }

I do believe you need to post the collection back to the server as you are saving the data and there is an editable text box here @Html.TextBoxFor(modelItem => item.Name) in your existing code.

Upvotes: 4

Ajay
Ajay

Reputation: 6590

Please make sure you have added @Scripts.Render("~/bundles/jqueryval") in your layout.

Add @Html.ValidationMessageFor(m => item.Name) and make sure you have added

@Html.ValidationSummary(true, "", new { @class = "text-danger" })

Edit

You have to add ModelState.AddModelError("key", "message");

try to change and see what happen

@Html.ValidationSummary(true, "", new { @class = "text-danger" })

to

@Html.ValidationSummary(false, "", new { @class = "text-danger" })

Upvotes: 0

Related Questions