Reputation: 12855
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
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
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" })
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