ModelState.IsValid is always true - asp.net

As is typical in MVC, I have a View, a Model, and a Controller.
The View contains a form:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
<ul class="fieldlist">
<li>
#Html.LabelFor(m =>m.Email)
#Html.TextBoxFor(m => m.Email)
#Html.ValidationMessageFor(m => m.Email)
</li>
<li>
<div class="row">
<div class="col-md-6">
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="col-md-6">
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
</div>
</li>
<li>
#Html.LabelFor(m => m.PhoneNumber)
#Html.TextBoxFor(m => m.PhoneNumber)
#Html.ValidationMessageFor(m => m.PhoneNumber)
</li>
<li>
<div Class="row">
<div Class="col-md-2">
<input type="submit" value="Save" />
</div>
</div>
</li>
</ul>
}
The model looks like this:
public class UserAdminUserViewModel
{
[Display(Name = "User Database ID"), DataType(DataType.Text)]
public string Id { get; set; }
[Display(Name = "Full Name")]
public string Name { get { return FirstName + " " + LastName; } }
[Required()]
[Display(Name = "First Name"), DataType(DataType.Text)]
public string FirstName { get; set; }
[Required()]
[Display(Name = "Last Name"), DataType(DataType.Text)]
public string LastName { get; set; }
[Required()]
[Display(Name = "Email Address"), DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Display(Name = "Telephone Number"), DataType(DataType.Text)]
public string PhoneNumber { get; set; }
}
And the controller to receive the updates:
[HttpPost,ValidateAntiForgeryToken]
public ActionResult User(UserAdminUserViewModel model)
{
if (ModelState.IsValid)
{
// do something, apply the updates to the database, presumably
}
return View(model);
}
...but ModelState.IsValid is always true. The controller is receiving the entered information, but it appears that no validation is taking place. I have tried:
TryUpdateModel<UserAdminUserViewModel>(model);
and
UpdateModel<UserAdminUserViewModel>(model);
I'm not sure I see how these would work, but they were the first suggestions I came across. No dice. Still valid regardless of the data in 'model'. I also tried:
System.ComponentModel.DataAnnotations.Validator.ValidateObject(model, new System.ComponentModel.DataAnnotations.ValidationContext(model));
More promising, but still doesn't work. I also tried the more verbose approach:
System.ComponentModel.DataAnnotations.ValidationContext valContext = new System.ComponentModel.DataAnnotations.ValidationContext(model, null, null);
List<System.ComponentModel.DataAnnotations.ValidationResult> valResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
System.ComponentModel.DataAnnotations.Validator.TryValidateObject(model, valContext, valResults, true);
foreach(System.ComponentModel.DataAnnotations.ValidationResult result in valResults)
{
this.ModelState.AddModelError(result.MemberNames.FirstOrDefault() ?? string.Empty, result.ErrorMessage);
}
...which also didn't work. 'valResults', which should contain any errors in the state of the model, is never filled; no validation is happening.

I would use this for email validation:
#using using System.ComponentModel.DataAnnotations;
[Required]
[EmailAddress]
[Display(Name = "Email Address")]
public string Email { get; set; }
Please see this StackOverflow post
However, EmailAddressAttribute adds server-side validation on top of
that. I.e. there is no server-side validation if you only use
DataTypeAttribute!

If the problem with incorrect email address, here's the solution:
[Email(ErrorMessage = "Invalid Email Address")]
[Required()]
[Display(Name = "Email Address"), DataType(DataType.EmailAddress)]
public string Email { get; set; }
DataType(DataType.EmailAddress) means only how to show textbox on client-side, while Email attribute does actual validation.

Related

Validation in MVC 5 .net

Im try to give alert if there is a error in form in my form there are some text fields validation like below
<div class="form-group">
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-4 col-xm-12">
<label class="control-label form-text-align text-top-padding ">
#Resources.StandardPrice
</label>
</div>
<div class="col-lg-8 col-md-8 col-sm-8 col-xm-12 text-top-padding">
#Html.TextBoxFor(model => model.products.BasicPrice, new { #class = "form-control errorClass", #id = "basicPrice", #placeholder = #Resources.StandardPrice, #onblur = "addClass(this)", #maxlength = Resources.AddNewProductFieldMaxLength })
#Html.ValidationMessageFor(model => model.products.BasicPrice, null, new { #class = "help-inline" })
</div>
</div>
</div>
if there is some error in form how can I give a alert
In order to display the error message and prevent the submission of your form you have to add controls on your model (or ViewModel).
For example if you want that field to be required so that the form will not be submitted only if the user give a value to that field you have to add the [Required] to your product's model attribute "BasicPrice" as follow :
public class products {
public int ID { get; set; }
[Required]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
}
Here's a link to the Microsoft official Documentation which explain the subject and give more details :
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-validation-to-the-model
If you already did what #Mohamed Kamel Bouzekria suggested and still not working.
it's possible that you missing something in your controller which could this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult YOurMethod( Model model)
{
if (ModelState.IsValid)//if there is no errors and valid values
{
//do something
db.SaveChanges();
return RedirectToAction("Index");
}
return View();//else return the same view that should display the errors
}
if it still not working then you missing something else in your view.if so post the full code of the view

asp.Net MVC view model is empty on post

I have a complex view model that I am passing to a create view. When I enter data on the page and post it the model is empty. Both the fields in the sub-object and the "test" field are empty. Why?
public class ContactIncident
{
[Key]
public int Id { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Display(Name = "Incident Date")]
[DataType(DataType.Date)]
public DateTime? IncidentDateTime { get; set; }
[Display(Name = "Follow Up Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
[DataType(DataType.Date)]
public DateTime? FollowUpDate { get; set; }
}
public class IncidentManager
{
public ContactIncident Incident { get; set; }
public string Test { get; set; }
}
public ActionResult Create(int? id)
{
IncidentManager im = new IncidentManager();
ContactIncident ci = new ContactIncident();
ci.IncidentDateTime = DateTime.Now;
ci.FollowUpDate = DateTime.Now.AddDays(14);
im.Incident = ci;
return View(im);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IncidentManager im)
{
if (ModelState.IsValid)
{
ContactIncident ci = new ContactIncident();
ci.IncidentDateTime = incident.Incident.IncidentDateTime;
ci.Description = im.Incident.Description;
return RedirectToAction("Index");
}
return View(incident);
}
View:
#model MyApp.Web.ViewModels.IncidentManager
#{
ViewBag.Title = "Edit Incident";
}
<h4>#ViewBag.Title</h4>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal well">
#Html.ValidationSummary(true)
#Html.EditorFor(model=>model.Test)
<div class="row">
<div class="col-md-2">
#Html.LabelFor(model => model.Incident.IncidentDateTime)
</div>
<div class="col-md-2">
#Html.DisplayFor(model => model.Incident.IncidentDateTime)
</div>
</div>
<div class="row">
<div class="col-md-2">
#Html.LabelFor(model => model.Incident.Description)
</div>
<div class="col-md-10">
#Html.EditorFor(model => model.Incident.Description, new { htmlAttributes = new { #class = "form-control", rows = "5" }, })
</div>
<div class="col-md-2">
#Html.LabelFor(model => model.Incident.FollowUpDate)
</div>
<div class="col-md-2">
#Html.EditorFor(model => model.Incident.FollowUpDate, new { htmlAttributes = new { #class = "form-control"}, })
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
The problem is that the DefaultModelBinder won't be able to map nested models properly if you use a different parameter name. You must use the same parameter name as the ViewModel name.
public ActionResult Create(IncidentManager incidentManager)
As a general practice, always use the name of the model as the parameter name to avoid mapping problems.
UPDATE:
The DefaultModelBinder uses "convention based" mapping.
IncidentManager.Incident = incidentManager.Incident (will map)
IncidentManager.Incident = im.Incident //won't map because 'im' != 'incidentManager'

How do I render a group of checkboxes using MVC 4 and View Models (strongly typed)

I'm rather new to the ASP.net MVC world and I'm trying to figure out how to render a group of checkboxes that are strongly typed to a view model. In webforms I would just use the checkboxlist control but im a bit lost with MVC.
I'm building a simple contact form for a wedding planning business and need to pass whatever checkbox values the user selects to my controller.
The form checkboxes need to look like this:
Your help would be greatly appreciated. Thanks!
Here's what I have so far.
CONTROLLER
[HttpPost]
public ActionResult Contact(ContactViewModel ContactVM)
{
if (!ModelState.IsValid)
{
return View(ContactVM);
}
else
{
//Send email logic
return RedirectToAction("ContactConfirm");
}
}
VIEW MODEL
public class ContactViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
public IEnumerable<SelectListItem> SubjectValues
{
get
{
return new[]
{
new SelectListItem { Value = "General Inquiry", Text = "General Inquiry" },
new SelectListItem { Value = "Full Wedding Package", Text = "Full Wedding Package" },
new SelectListItem { Value = "Day of Wedding", Text = "Day of Wedding" },
new SelectListItem { Value = "Hourly Consultation", Text = "Hourly Consultation" }
};
}
}
//Not sure what I should do for checkboxes...
}
VIEW
#model NBP.ViewModels.ContactViewModel
#{
ViewBag.Title = "Contact";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<div id="ContactContainer">
<div><span class="RequiredField">* </span>Your Name:</div>
<div>
#Html.TextBoxFor(model => model.Name)
</div>
<div><span class="RequiredField">* </span>Your Phone:</div>
<div>
#Html.TextBoxFor(model => model.Phone)
</div>
<div><span class="RequiredField">* </span>Your Email:</div>
<div>
#Html.TextBoxFor(model => model.Email)
</div>
<div>Subject:</div>
<div>
#Html.DropDownListFor(model => model.Subject, Model.SubjectValues)
</div>
<div>Vendor Assistance:</div>
<div>
<!-- CHECKBOXES HERE -->
</div>
<div>
<input id="btnSubmit" type="submit" value="Submit" />
</div>
</div>
}
You could enrich your view model:
public class VendorAssistanceViewModel
{
public string Name { get; set; }
public bool Checked { get; set; }
}
public class ContactViewModel
{
public ContactViewModel()
{
VendorAssistances = new[]
{
new VendorAssistanceViewModel { Name = "DJ/BAND" },
new VendorAssistanceViewModel { Name = "Officiant" },
new VendorAssistanceViewModel { Name = "Florist" },
new VendorAssistanceViewModel { Name = "Photographer" },
new VendorAssistanceViewModel { Name = "Videographer" },
new VendorAssistanceViewModel { Name = "Transportation" },
}.ToList();
}
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
public IEnumerable<SelectListItem> SubjectValues
{
get
{
return new[]
{
new SelectListItem { Value = "General Inquiry", Text = "General Inquiry" },
new SelectListItem { Value = "Full Wedding Package", Text = "Full Wedding Package" },
new SelectListItem { Value = "Day of Wedding", Text = "Day of Wedding" },
new SelectListItem { Value = "Hourly Consultation", Text = "Hourly Consultation" }
};
}
}
public IList<VendorAssistanceViewModel> VendorAssistances { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new ContactViewModel());
}
[HttpPost]
public ActionResult Index(ContactViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
//Send email logic
return RedirectToAction("ContactConfirm");
}
}
View:
#using (Html.BeginForm())
{
<div id="ContactContainer">
<div><span class="RequiredField">* </span>Your Name:</div>
<div>
#Html.TextBoxFor(model => model.Name)
</div>
<div><span class="RequiredField">* </span>Your Phone:</div>
<div>
#Html.TextBoxFor(model => model.Phone)
</div>
<div><span class="RequiredField">* </span>Your Email:</div>
<div>
#Html.TextBoxFor(model => model.Email)
</div>
<div>Subject:</div>
<div>
#Html.DropDownListFor(model => model.Subject, Model.SubjectValues)
</div>
<div>Vendor Assistance:</div>
<div>
#for (int i = 0; i < Model.VendorAssistances.Count; i++)
{
<div>
#Html.HiddenFor(x => x.VendorAssistances[i].Name)
#Html.CheckBoxFor(x => x.VendorAssistances[i].Checked)
#Html.LabelFor(x => x.VendorAssistances[i].Checked, Model.VendorAssistances[i].Name)
</div>
}
</div>
<div>
<input id="btnSubmit" type="submit" value="Submit" />
</div>
</div>
}
Use a string array in your view model. You can then use the helper I hacked together. if you don't want to use the helper and the enum then see the actual Html at the bottom. The binder will return a string array with only the selected string values in it. if none are selected it returns a null value for your array. You must account for that, you have been warned :)
View Model:
[Display(Name = "Which Credit Cards are Accepted:")]
public string[] CreditCards { get; set; }
Helper:
public static HtmlString CheckboxGroup<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> propertySelector, Type EnumType)
{
var groupName = GetPropertyName(propertySelector);
var modelValues = ModelMetadata.FromLambdaExpression(propertySelector, htmlHelper.ViewData).Model;//propertySelector.Compile().Invoke(htmlHelper.ViewData.Model);
StringBuilder literal = new StringBuilder();
foreach (var value in Enum.GetValues(EnumType))
{
var svalue = value.ToString();
var builder = new TagBuilder("input");
builder.GenerateId(groupName);
builder.Attributes.Add("type", "checkbox");
builder.Attributes.Add("name", groupName);
builder.Attributes.Add("value", svalue);
var contextValues = HttpContext.Current.Request.Form.GetValues(groupName);
if ((contextValues != null && contextValues.Contains(svalue)) || (modelValues != null && modelValues.ToString().Contains(svalue)))
{
builder.Attributes.Add("checked", null);
}
literal.Append(String.Format("</br>{1} <span>{0}</span>", svalue.Replace('_', ' '),builder.ToString(TagRenderMode.Normal)));
}
return (HtmlString)htmlHelper.Raw(literal.ToString());
}
private static string GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> propertySelector)
{
var body = propertySelector.Body.ToString();
var firstIndex = body.IndexOf('.') + 1;
return body.Substring(firstIndex);
}
HTML:
#Html.CheckboxGroup(m => m.CreditCards, typeof(VendorCertification.Enums.CreditCardTypes))
Use this if helper extensions scare you:
<input id="CreditCards" name="CreditCards" type="checkbox" value="Visa"
#(Model.CreditCards != null && Model.CreditCards.Contains("Visa") ? "checked=true" : string.Empty)/>
<span>Visa</span><br />
<input id="CreditCards" name="CreditCards" type="checkbox" value="MasterCard"
#(Model.CreditCards != null && Model.CreditCards.Contains("MasterCard") ? "checked=true" : string.Empty)/>
<span>MasterCard</span><br />
For me this works too, and I think this is the simplest (reading the previous answers).
The viewmodel has a string[] for the check boxes.
public string[] Set { get; set; }
The view has this code, and you can repeat the input as many times you need. name, id of the input control has to match the name of the property of the viewmodel.
<div class="col-md-3">
<div class="panel panel-default panel-srcbox">
<div class="panel-heading">
<h3 class="panel-title">Set</h3>
</div>
<div class="panel-body">
<div class="form-group-sm">
<label class="control-label col-xs-3">1</label>
<div class="col-sm-8">
<input type="checkbox" id="Set" name="Set" value="1" />
</div>
<label class="control-label col-xs-3">2</label>
<div class="col-sm-8">
<input type="checkbox" id="Set" name="Set" value="2" />
</div>
</div>
</div>
</div>
</div>
On the post method the Set variable is an array, having the checked value(s).

Remote Validate for DropDownList, MVC3, not firing in my case

I am using ASP.NET MVC3 and EF 4.1
I have two DropDownList in my Model, It is required and not duplicated too.
And I want the Remote validate function: ValidateDuplicateInsert get firing when user submit data. But I can NOT get the ValidateDuplicateInsert function firing.
Where am I wrong?
My Model
[Key]
public int CMAndOrgID { get; set; }
[Display(Name = "CM")]
[Required(ErrorMessage = "CM is required.")]
[Remote("ValidateDuplicateInsert", "CMAndOrg", HttpMethod = "Post", AdditionalFields = "CMID, OrganizationID", ErrorMessage = "CM is assigned to this Organization.")]
public int? CMID { get; set; }
[Display(Name = "Organization")]
[Required(ErrorMessage = "Organization is required.")]
public int? OrganizationID { get; set; }
public virtual CM CM { get; set; }
public virtual Organization Organization { get; set; }
The ValidateDuplicateInsert function in my CMAndOrg controller
[HttpPost]
public ActionResult ValidateDuplicateInsert(string cmID, string orgID)
{
bool flagResult = true;
foreach (CMAndOrg item in db.CMAndOrgs)
{
if (item.CMID.ToString() == cmID && item.OrganizationID.ToString() == orgID)
{
flagResult = false;
break;
}
}
return Json(flagResult);
}
And my View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CMAndOrg</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CMID, "CM")
</div>
<div class="editor-field">
#Html.DropDownList("CMID", String.Empty)
#Html.ValidationMessageFor(model => model.CMID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.OrganizationID, "Organization")
</div>
<div class="editor-field">
#Html.DropDownList("OrganizationID", String.Empty)
#Html.ValidationMessageFor(model => model.OrganizationID)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
There is a bug in MVC3 related to unobtrusive validation on dropdownlist. Please reference to this http://aspnet.codeplex.com/workitem/7629[^] link for more detail explaination.
Briefly, you can't use the same name for category collection and category field, so just change your collection name and update following line in your view
#Html.DropDownList("CategoryID", String.Empty)
with this
#Html.DropDownListFor(model => model.CategoryID, new SelectList((System.Collections.IEnumerable)ViewData["Categories"], "Value", "Text"))
Thanks again Henry He
Original link
http://www.codeproject.com/Articles/249452/ASP-NET-MVC3-Validation-Basic?msg=4330725#xx4330725xx

ASP.NET MVC 3 login page by using partial

I want to use Logon page in on home page partially (which is index page).
Then my homepage has UserName textbox, Password textbox , RememberMe checkbox and button for login and register which means Logon page is partial view.
When I run the homepage and if I click Login button, it does not work validation error even
I linked JavaScript and I put [Required] on model class.
I don't know how can I enable the validation error with JavaScript?
And how can I use the HomeController? (Homepage is default page)
Could you help me?
I am beginner, and I am just started to develop MVC application.
Index.cshtml on Home folder
<div id="border_frame">
#Html.Partial("_LogOnPartial")
</div>
LogonPartial.cshtml on Shared folder
#model MvcApplication2.Models.LogOnModel
#if (Request.IsAuthenticated)
{
<text>Welcome <strong>#User.Identity.Name</strong>!
[ #Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
}
else
{
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
using (Html.BeginForm())
{
<div>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { style = "width:150px;" })
#Html.ValidationMessageFor(m => m.UserName)
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { style = "width:150px;" })
#Html.ValidationMessageFor(m => m.Password)
<div>
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
<button name="button" class="login_button" value="logIn">Log In</button>
<button name="button" class="register_button" value="register">Register now</button>
</div>
#Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")
}
}
AccountModel.cs
public class LogOnModel
{
[Required]
[Display(Name = "User ID")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
HomeController.cs
public ActionResult Index()
{
return View();
}
You have to have a post action method to handle your postback.
[HttpPost]
public ActionResult Index(LogonModel model)
{
if (ModelState.IsValid) {
// do your validation stuff
}
// If you don't return the model, validation won't work.
return View(model);
}

Resources