Client or server validation doesn't work with Data Annotations - asp.net-mvc

I'm developing a MVC 4 application with Entity Framework 6 and encountered a problem with Data Annotations. No matter what annotation I use, it isn't validate.
I'm using the following view model to render a form:
public class UserViewModel
{
[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Name")]
[StringLength(100, MinimumLength = 2)]
public string FirstName { get; set; }
...
}
In the view:
#Html.LabelFor(m => m.FirstName)
#Html.EditorFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
And it gets rendered:
<label for="FirstName">First Name</label>
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
When I click the submit button the client validation doesn't get triggered and in the controller the ModelState.IsValid is true.
UPDATE:
Controller:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
return View(model);
}
[HttpPost]
public ActionResult Edit(UserViewModel model)
{
if (ModelState.IsValid == true)
{
_userService.Save(model);
return View(model);
}
return View(model);
}

The problem is that you POST to your controller, you map to User object, not to UserViewModel. This is one of the reasons your server-side validation is not triggered.
As for the client-side validation, I'm not sure, but could be just the same error - your view is based on User type, rather than on UserViewModel.
So your controller should look like this:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
var viewModel = new UserViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
// other properties
};
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(UserViewModel viewModel)
{
if (ModelState.IsValid == true)
{
var model = new User()
{
FirstName = viewModel.FirstName,
LastName = viewModel.LastName,
// other properties
};
_userService.Save(model);
return View(model);
}
return View(model);
}
And top of your Edit.cshtml you should have #model UserViewModel.
Hope this helps

After recreating my project I found out that the problem was the Ninject.Web.MVC dll. I didn't use this dll in my project so I removed it and it worked.

Related

MVC model validation

So, im currently building an application that needs the user model validating, and if the incorrect properties are filled in to the user it will tell them.
I have the data annotations set up, but im not sure how i relay the error message back to the user?
I have this set up so far on my model and view.
Model
public class DatabaseModel
{
[Required(ErrorMessage = ("A first name is required"))]
public string FirstName { get; set; }
[Required(ErrorMessage = ("A last name is required"))]
public string LastName { get; set; }
[Required(ErrorMessage = ("A valid role is required"))]
public string Role { get; set; }
// TODO - Validate rank to only b 1 - 10
//
[Range(1,10, ErrorMessage = ("A rank between 1 and 10 is required"))]
public int Rank { get; set; }
}
And View
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.Label("First Name")
<br>
#Html.TextBoxFor(m => m.FirstName)
<br>
#Html.Label("Last Name")
<br>
#Html.TextBoxFor(m=>m.LastName)
<br>
#Html.Label("Role")
<br>
#Html.TextBoxFor(m => m.Role)
<br>
#Html.Label("Rank")
<br>
#Html.TextBoxFor(m => m.Rank)
<br><br>
<input type="submit" value="Save">
}
My Controller
public class HomeController : Controller
{
// GET: Home
[HttpGet]
public ActionResult Index()
{
DatabaseModel model = new DatabaseModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View();
}
}
I believe the model needs to be passed back to the view in order to display the error message, and although i have read through the documentation on asp.net i cannot understand how they just add the error message and the form knows how to display the errors to the user.
I am extremely confused.
You need to use ModelState.IsValid in your Controller and also #Html.ValidationMessageFor(model => model.FirstName) in your view:
public ActionResult Index(ViewModel _Model)
{
// Checking whether the Form posted is valid one.
if(ModelState.IsValid)
{
// your model is valid here.
// perform any actions you need to, like database actions,
// and/or redirecting to other controllers and actions.
}
else
{
// redirect to same action
return View(_Model);
}
}
For your example:
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.LabelFor(m=>m.FirstName)
<br>
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
<br>
#Html.LabelFor(m=>m.LastName)
<br>
#Html.TextBoxFor(m=>m.LastName)
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
. . .
<input type="submit" value="Save">
}
Controller:
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View(model);
}

BeginForm in ChildAction uses wrong id

There is something simple I don't understand with ChildActions.
I've created a simple View for a model, that loads a child action with a form.
The child action has another model than its parent, with a different id property.
Html.HiddenFor(m => m.Id) still outputs the parents id, although #Model.id outputs the correct value!
Can't I reliably use the Helper methods in ChildActions, or is this a known bug?
HomeController
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new Models.HomeModel { id = 1, message = "bugmodel" };
return View(model);
}
[HttpGet]
[ChildActionOnly]
public ActionResult Child(int id)
{
var model = new Models.HomeChildModel { id = 100, parentId = id, childMessage = "My Child message" };
return PartialView(model);
}
[HttpPost]
[ActionName("Child")]
[ValidateAntiForgeryToken()]
public ActionResult ChildPost(Models.HomeChildModel model)
{
return RedirectToAction("Index");
}
}
Models
public class HomeModel
{
public int id { get; set; }
public string message { get; set; }
}
public class HomeChildModel
{
public int id { get; set; }
public int parentId { get; set; }
public string childMessage { get; set; }
}
Home view
#model ChildActionBug.Models.HomeModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.DisplayFor(m=>m.id)
#Html.DisplayFor(m=>m.message)
#Html.Action("Child", new { id = Model.id })
**Child view**
#model ChildActionBug.Models.HomeChildModel
<h3>Child here</h3>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m=>m.id)
#Html.HiddenFor(m=>m.parentId)
#Html.EditorFor(m=>m.childMessage)
<div>Child Model ID: #Model.id</div>
<button type="submit">Save</button>
}
Based on the answer given in the SO question I posted in the comment, you're better off explicitly creating the hidden fields
ASP.Net MVC Html.HiddenFor with wrong value
That's normal and it is how HTML helpers work. They first use the
value of the POST request and after that the value in the model. This
means that even if you modify the value of the model in your
controller action if there is the same variable in the POST request
your modification will be ignored and the POSTed value will be used.
So instead, hand craft the hidden fields:
<input type="hidden" name="Id" value="#Model.Id" />
<input type="hidden" name="ParentId" value="#Model.ParentId" />
<input type="hidden" name="ChildMessage" value="#Model.ChildMessage" />

UpdateModel not updating my model

I must have something incorrectly setup as I can't get the UpdateModel function to correctly update my model based on information passed in via a FormCollection.
My View looks like:
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
<p>ID: #Html.TextBox("ID", Model.ID)</p>
<p>Forename: #Html.TextBox("Forename", Model.Forename)</p>
<p>Surname: #Html.TextBox("Surname", Model.Surname)</p>
<input type="submit" value="Submit" />
}
</fieldset>
My model is:
namespace NSLM.Models
{
public class Person
{
public int ID;
public string Forename;
public string Surname;
}
}
and my controller is:
[HttpPost]
public ActionResult Details(FormCollection collection)
{
try
{
// TODO: Add update logic here
Models.Person m = new Models.Person();
// This doesn't work i.e. the model is not updated with the form values
TryUpdateModel(m);
// This does work
int.TryParse(Request.Form["ID"], out m.ID);
m.Forename = Request.Form["Forename"];
m.Surname = Request.Form["Surname"];
return View(m);
}
catch
{
return View();
}
}
as you can see if I manually assign each property it works fine, so what have I not set that would get the model to be updated with the form values?
Thanks,
Mark
Replace fields with properties in your model, i.e.:
namespace NSLM.Models
{
public class Person
{
public int ID {get; set;}
public string Forename {get; set;}
public string Surname {get; set;}
}
}
By the time the call gets to the action method any automatic model binding has already been performed. Try changing the input parameter of your action method to accept a Person instance. In that case the model binder will try to create the instance and populate it from the values passed by your form.
try this :
view :
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
<p>Forename: #Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</p>
<p>Surname: #Html.EditorFor(model => model.Surname)
#Html.ValidationMessageFor(model => model.Surname)
</p>
<input type="submit" value="Submit" />
}
</fieldset>
Controller :
[HttpPost]
public ActionResult Details(Person p)
{
if (ModelState.IsValid)
{
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(p);
}

MVC checkbox comeing back null

I have a checkbox, but the form is being submitted the value ticked are not being submited...
Html:
#foreach (var radiobutton in Model.InterestedIn)
{
<span > #Html.CheckBox("selected", radiobutton)
<label>#radiobutton</label></span>
<br />
}
Model:
[Display(Name = "Would you be interested in receiving *")]
public IList<string> InterestedIn { get; set; }
Controller:
IList<string> lists = new List<string>();
lists.Insert(0, "Latest News");
lists.Insert(1, "Special Offers");
lists.Insert(1, "New Products");
model.InterestedIn = lists;
PostMethod:
[HttpPost]
public ActionResult Index(Competition model)
{
if (ModelState.IsValid)
{
I don't that your code will compile at all. The CheckBox helper expects a boolean as second argument whereas you are passing it a string.
Try like this:
#model MyViewModel
#using (Html.BeginForm())
{
foreach (var value in Model.InterestedIn)
{
<span>
<input type="checkbox" name="interestedin" value="#Html.AttributeEncode(value)" />
<label>#value</label>
</span>
<br />
}
<button type="submit">OK</button>
}
This assumes that you have the following view model:
public class MyViewModel
{
[Display(Name = "Would you be interested in receiving *")]
public IList<string> InterestedIn { get; set; }
}
and the following controller:
public class HomeController : Controller
{
public ActionResult Index()
{
IList<string> lists = new List<string>();
lists.Insert(0, "Latest News");
lists.Insert(1, "Special Offers");
lists.Insert(1, "New Products");
var model = new MyViewModel();
model.InterestedIn = lists;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
If you want to use the CheckBox or even better the CheckBoxFor helper you will have to adapt your view model so that it no longer has an IList<string> property but an IList<CheckBoxItemViewModel> property where CheckBoxItemViewModel is another view model that will contain the label and a boolean property indicating whether this value has been selected or not.

DisplayAttribute turns off validation message

So when I have a DisplayAttribute decorating a property in one of my models...
[Required, Display(Name = "Some Name")]
public string SomeProperty { get; set; }
I no longer get a validation message for the field when using the ValidationMessageFor helper
#Html.ValidationMessageFor(model => model.SomeProperty)
And what's odd is if I use the overload that specifies a message, I still don't get the message. Anyone know what's going on here?
Unable to repro.
Model:
public class MyViewModel
{
[Required, Display(Name = "Some Name")]
public string SomeProperty { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SomeProperty)
#Html.EditorFor(x => x.SomeProperty)
#Html.ValidationMessageFor(x => x.SomeProperty)
<input type="submit" value="OK" />
}
When the form is submitted the validation error message is correctly shown if the field is left blank.

Resources