ModelState.IsValid returns false when it should be true - validation

I have a pretty simple MVC 2 form. It has two dropdowns, user and role. The employee dropdown passes validation, and the role dropdown does not, regardless of what I select. There is no default "empty" option although I plan to implement one, which is why I need the validation to work. It fails both client and server validation. I just can't see why one would work and one does not!
The Form:
<% using (Html.BeginForm()) {%>
<%:Html.ValidationSummary(true) %>
<%:Html.EditorFor(model => model.User, new { AllEmployees = Model.AllEmployees, RoleList = Model.RoleList })%>
<p>
<input type="submit" value="Add New User" />
</p>
<% } %>
<% Html.EndForm(); %>
The Editor Template:
<tr>
<td>
<div class="editor-label">
<%: Html.LabelFor(model => model.UserId) %>
<%: Html.RequiredMarkFor(model => model.UserId) %>
</div>
</td>
<td>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.UserId, new SelectList(ViewData["AllEmployees"] as IEnumerable, "UserId", "DisplayName", Model.UserId)) %>
<%: Html.ValidationMessageFor(model => model.UserId>
</div>
</td>
</tr>
<tr>
<td>
<div class="editor-label">
<%: Html.LabelFor(model => model.AccessLevel)%>
<%: Html.RequiredMarkFor(model => model.AccessLevel)%>
</div>
</td>
<td>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.AccessLevel, new SelectList(ViewData["RoleList"] as IEnumerable, Model.AccessLevel))%>
<%: Html.ValidationMessageFor(model => model.AccessLevel)%>
</div>
</td>
</tr>
The Metadata:
[DisplayName("Employee")]
[Required(ErrorMessage = "Please select an employee.")]
[StringLength(8, ErrorMessage = "User Id must be less than 8 characters.")]
[DisplayFormat(ConvertEmptyStringToNull = false,
HtmlEncode = true)]
[DataType(DataType.Text)]
public object UserId { get; set; }
// Validation rules for Access Level
[DisplayName("Role")]
[Required(ErrorMessage = "Please select the role for this user.")]
[StringLength(15, ErrorMessage = "Role must be under 15 characters.")]
[DisplayFormat(ConvertEmptyStringToNull = false,
HtmlEncode = true)]
[DataType(DataType.Text)]
public object AccessLevel { get; set; }
The Get Action:
List<String> roles = (from o in txDB.Users
select o.AccessLevel).Distinct().ToList();
var viewModel = new UserViewModel
{
User = new User(),
AllEmployees = empList,
RoleList = roles
};
return View(viewModel);
The Post Action:
[HttpPost]
[AuthorizeAttribute(Roles="Administrator")]
public ActionResult Create(User user)
{
if(!ModelState.IsValid)
{
//ModelState is invalid
return View(new User());
}
try
{
//do stuff
}
}
The Required Helper Method (from Define markup for [Required] fields in View in ASP.NET MVC 2.0):
public static string RequiredMarkFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
if(ModelMetadata.FromLambdaExpression(expression, helper.ViewData).IsRequired)
return "*";
else
return string.Empty;
}

Post method should be as follows to get Server side validation...
[HttpPost]
[AuthorizeAttribute(Roles="Administrator")]
public ActionResult Create(User user)
{
if(!TryUpdateModel(user))
{
// Model is INVALID
return View(user);
}
else
{
// ModelState is VALID
// Do stuff
}
}
The else might be redundant depending on what you're doing but that should get you going.
In the view above your <% using Html.BeginForm() %> you need
<% Html.EnableClientValidation(); %>
You also need to reference the scripts, MicrosoftAjax and MicrosoftMvcValidation I think

First of all: You have two closing form tags
If you use
<% using (Html.BeginForm()) {%>
<% } %>
you dont need to use this
<% Html.EndForm(); %>
Regarding your validation problem you are using an editor only for your User property, which is the only one that get binded by the model binder
<%:Html.EditorFor(model => model.User, new { AllEmployees = Model.AllEmployees, RoleList = Model.RoleList })%>
Try to replace the previous code with an EditorForModel as your Editor Template is for a model class.
So your form should change in
<% using (Html.BeginForm()) {%>
<%:Html.ValidationSummary(true) %>
<table>
<%:Html.EditorForModel()%>
</table>
<p>
<input type="submit" value="Add New User" />
</p>
<% } %>
and you're done!

Related

Checkboxlist - on Post: Create - NullReferenceException

StudentModel.
namespace mvcApp.Models
{
public class StudentModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Email Address")]
public string EmailAddress { get; set; }
public List<SchoolOrganization> Organizations { get; set; }
}
public class SchoolOrganization
{
public string Name { get; set; }
public bool IsInvolved { get; set; }
}
}
Student is involved in multiple organizations.
Controller
namespace mvcApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult StudentInformation()
{
// populate student with data
var student = new StudentModel() { FirstName = "Joe", LastName = "Doe", EmailAddress = "jdoe#hotmail.com"};
// Populate with Organizations
student.Organizations = new List<SchoolOrganization>();
student.Organizations.Add(new SchoolOrganization() { Name = "Math Club", IsInvolved = true});
student.Organizations.Add(new SchoolOrganization() { Name = "Chess Club", IsInvolved = false });
student.Organizations.Add(new SchoolOrganization() { Name = "Football", IsInvolved = true });
return View(student);
}
**[HttpPost]
public ActionResult StudentInformation(StudentModel student)
{
Response.Write("Name: " + student.FirstName);
foreach (var o in student.Organizations)
{
Response.Write(o.Name + " : " + o.IsInvolved.ToString());
}
return View();
}**
}
}
Data will be eventually populated from database.
View
#model mvcApp.Models.StudentModel
#{
ViewBag.Title = "StudentInformation";
}
<h2>StudentInformation</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>StudentModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div>
<table>
<tr>
<td>Organization Name</td><td>Is Involved</td>
</tr>
#for (int i = 0; i < Model.Organizations.Count; i++) <== System.NullReferenceException here
{
#Html.HiddenFor(m => m.Organizations[i].IsInvolved)
<tr>
<td>#Html.DisplayFor(m => m.Organizations[i].Name)</td>
<td>#Html.CheckBoxFor(m => m.Organizations[i].IsInvolved)</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The above code displays fine with HttGet. However, when i try to update i get System.NullReferenceException. https://www.dropbox.com/s/dz0bg3hkd0yq8e3/studentInformation.png?dl=0
Can anyone please help figure it what's going on?
Thank you.
In the code sample you have provided; the HomeController's [HttpPost] ActionResult for StudentInformation does not create and pass a new instance of the updated object model to the view, but instead runs a basic debug.writeline routine. As a result the dependent view for the [HttpPost] view of "StudentInformation.cshtml", does not receive a populated instance of the updated model...
Setting a breakpoint on the first line of the model for the "StudentInformation.cshtml" page and running the application will demonstrate that the model referenced at the top of the page, has no data within it...
This is why the [HttpPost] version of the page simply renders the blank model, without any altered data values you may have created, UNTIL it gets to the section where the view is dependent on a count of new data values which must be present within the model that is called at first line of the page... to continue.
An empty data model set results in a null reference because there are no values to count within it.
In order to view an updated group of settings, The [HttpPost] version of the view model must be passed an instance of the model that returns the new information as in "return View(nameOfViewDataSet)" (you build a data set again, and pass it as a new version of the model, with revised form data present).
Until there is data passed via the return View statement relative to the [HttpPost] version of the StudentInformation ActionResult, to the actual view, there will be no display data, and the count function will continue to return a null value.
I hope that is helpful.

How to make use of Viewbag in my View

I dont understand, I have my in my Controller:
[HttpGet]
public ActionResult Detail(int userId)
{
var user = ZincService.GetUserForId(userId);
if (user != null)
{
ViewBag.user = userId;
ViewBag.email = user.Email;
ViewBag.title = user.JobTitle;
ViewBag.node = user.Node;
}
return View(user);
}
then my view, Detail.aspx
<div id="user-details-view">
<div>
Title:
</div>
<div>
<%: Model.JobTitle %>
<%: Model.News %>
<%: Model.Node %>
</div>
<div>
<%: Html.ActionLink("Change Email Address", "ChangeEmailAddress", new { #id = Model.UserId })%>
</div>
</div>
when I run my app i get an error:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Areas/Admin/Views/User/Detail.aspx
I dont understand? Is it because of syntax errors?
Previous posts are correct as in logic but you assigned the viewbag names in your controller differently. It should be:
<div id="user-details-view">
<div>
Title:
</div>
<div>
<%: ViewBag.email %>
<%: ViewBag.title %>
<%: ViewBag.node %>
</div>
<div>
<%: Html.ActionLink("Change Email Address", "ChangeEmailAddress", new { #id = ViewBag.user })%>
</div>
</div>
Hope it helps..
It must be like this
<%: ViewBag.JobTitle %>
<%: ViewBag.News %>
<%: ViewBag.Node %>
Replace the Model. with ViewBag.
<%: ViewBag.title %>
<%: ViewBag.email %>
<%: ViewBag.node %>
and also change this
<%: Html.ActionLink("Change Email Address", "ChangeEmailAddress", new { id = ViewBag.user })%>
You must use that sintax:
<%: ViewBag.email %>
<%: ViewBag.title %>
<%: ViewBag.node %>
But would be better if you use Model:
public class UserInfo
{
public int UserId { get; set; }
public string Email { get; set; }
public string Title { get; set; }
public NodeType Node { get; set; }
}
[HttpGet]
public ActionResult Detail( int userId )
{
var data = ZincService.GetUserForId( userId );
var user = new UserInfo();
if ( data != null )
{
user.UserId = userId;
user.Email = data.Email;
user.Title = data.JobTitle;
user.Node = data.Node;
}
return View( user );
}
In view (MVC3) with razor sintax:
#model UserInfo
<div id="user-details-view">
<div>
Title:
</div>
<div>
#Model.Title
#Model.Node
</div>
<div>
#Html.ActionLink("Change Email Address", "ChangeEmailAddress", new { #id = Model.UserId })
</div>
</div>

Return Succes Message After Delete/Update/create

This is my GesAgence Page action :
public ActionResult GesAgence()
{
var test = new Models.J2VEntities();
return View(test.agence);
}
This is my Action for Deleting :
public ActionResult DeleteAg(string id)
{
Models.J2VEntities entity = new Models.J2VEntities();
Models.agence model = (from p in entity.agence
where p.Idag == id
select p).SingleOrDefault();
//Sauvgarde ds la BD
entity.agence.DeleteObject(model);
entity.SaveChanges();
return View("gesAgence");
}
So i'm wondring how to return Succes message after deleting(i tried with TempData but didn't succed because my gesAgence must return model not TempData).
You can use Ajax to call your controller from your viewpage and pop up the message whatever your controller returned, try something like this.
Script on your view page.
function onDeleteAg (id) {
var answer = confirm("Are you sure you want to delete AG ?")
if (answer) {
$.ajax(
{
type: "Get",
url: '<%= Url.Action("DeleteAg","YourControllerName") %>',
data: { agId: id },
success: function (data) {
//HERE--data is the message you that your controller DeleteAg method will return after it's called. you need to do something here to display this message(data) anywhere you want to . something like below.
alert(data);
},
error: (function () { alert("Error! Ag was not deleted." ); })
});
}
};
Method on your controller.
public string DeleteAg(string agId)
{
try{
Models.J2VEntities entity = new Models.J2VEntities();
Models.agence model = (from p in entity.agence
where p.Idag == id
select p).SingleOrDefault();
//Sauvgarde ds la BD
entity.agence.DeleteObject(model);
entity.SaveChanges();
}
catch(Exception ex)
{
return "AG has not been deleted successfully;
}
return "AG has been deleted successfully;
}
you can call this method via ajax and return JsonResult instead of ActionResult, by looking result you can show message to user.
public JsonResult DeleteAg(string id)
{
Models.J2VEntities entity = new Models.J2VEntities();
Models.agence model = (from p in entity.agence
where p.Idag == id
select p).SingleOrDefault();
//Sauvgarde ds la BD
entity.agence.DeleteObject(model);
entity.SaveChanges();
var json = new
{
success = true
};
return Json(json);
}
You can set Success to ViewBag
public ActionResult DeleteAg(string id)
{
Models.J2VEntities entity = new Models.J2VEntities();
Models.agence model = (from p in entity.agence
where p.Idag == id
select p).SingleOrDefault();
//Sauvgarde ds la BD
entity.agence.DeleteObject(model);
entity.SaveChanges();
ViewData["Success"] = true;
return View("gesAgence");
}
in view
#if(ViewData["Success"] != null && (bool)ViewData["Success"]){
<script>alert("Sucess!");</script>
}
This is my view :
<% if(ViewData != null && ViewData["Success"] != null && (bool)ViewData["Success"]){ %>
<script type="text/javascript"> alert("Sucess!");</script>
<% } %>
<div class="clear">
</div>
<div id="main">
<h1> Demande preinscrit</h1>
<ul class="listing">
<% foreach (var item in Model) { %>
<li>
<div class="listinfo">
<h3>
<%: Html.DisplayFor(modelItem => item.Nomag) %>
</h3>
<p>
<%: Html.DisplayFor(modelItem => item.Idag) %>
</p>
<span class="price"> <%: Html.DisplayFor(modelItem => item.Adrag) %> <%: Html.DisplayFor(modelItem => item.Vilag) %> <%: Html.DisplayFor(modelItem => item.Gov) %></span> <span class="media">Tel : <%: Html.DisplayFor(modelItem => item.Telag) %> |</span> <%: Html.DisplayFor(modelItem => item.Mailag) %>
</div>
<div class="listingbtns">
<span class="listbuttons"><%: Html.ActionLink("Bloque", "Bloque", new {id= item.Idag}) %> </span>
<span class="listbuttons"><%: Html.ActionLink("Supprime", "DeleteAg", new { id = item.Idag })%></span>
</div>
<div class="clear">
</div>
</li>
<% } %>
i got this error : System.NullReferenceException: Object reference not set to an instance of an object on this line <% foreach (var item in Model) { %>.

Retrieve a subset of a ViewModel from a RenderPartial on POST

I have a ViewModel which contains a child ViewModel. In a strongly typed Viewed of the parent, I want to RenderPartial on the child, and have results persist following POST.
But the child fields are always null.
This should work, shouldn't it? I am fairly new to MVC, hopefully I'm missing something simple. Hopefully someone can point it out!
Thanks!
Example
ViewModels
public class EggBoxViewModel
{
public string Brand { get; set; }
public int Price { get; set; }
public EggViewModel Egg { get; set; }
}
public class EggViewModel
{
public string Size { get; set; }
public bool IsBroken { get; set; }
}
Controller
public ActionResult Demo()
{
EggBoxViewModel eggBox = new EggBoxViewModel();
eggBox.Brand = "HappyEggs";
eggBox.Price = 3;
EggViewModel egg = new EggViewModel();
egg.Size = "Large";
egg.IsBroken = false;
eggBox.Egg = egg;
return View(eggBox);
}
[HttpPost]
public ActionResult Demo(EggBoxViewModel eggBox)
{
// here, eggBox.Egg is null
}
Views
"Demo"
#model MvcApplication1.ViewModels.EggBoxViewModel
#using (Html.BeginForm())
{
<h2>EggBox:</h2>
<p>
#Html.LabelFor(model => model.Brand)
#Html.EditorFor(model => model.Brand)
</p>
<p>
#Html.LabelFor(model => model.Price)
#Html.EditorFor(model => model.Price)
</p>
<p>
#{Html.RenderPartial("_Egg", Model.Egg);}
</p>
<input type="submit" value="Submit" />
}
"_Egg" (Partial)
#model MvcApplication1.ViewModels.EggViewModel
<h2>Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.EditorFor(model => model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBoxFor(model => model.IsBroken)
</p>
Use an Editor Template, in most cases they're better for rendering child objects or collections. In your Views\Shared folder create a new folder called EditorTemplates if you don't already have one. Add a new partial view called EggViewModel and use the same code as in your partial view. This is the bit of magic that renders and names all your fields correctly. It will also handle a collection of EggViewModels without a for each loop as the Editor template will automatically render all the items passed in a collection:
#model MvcApplication1.ViewModels.EggViewModel
<h2>Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.EditorFor(model => model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBoxFor(model => model.IsBroken)
</p>
Then in your Demo View use your new Editor Template instead of the partial:
<p>
#Html.EditorFor(x => x.Egg)
</p>
Here are the fields that are rendered:
In the post back you can that the EggViewModel is now part of the EggBoxViewModel:
Also in the ModelState you can see that the Egg fields are prefixed with the property name used in the EggBoxViewModel which makes them a subset of EggBox.
And...the great thing is that if you want to have a collection of Eggs in your EggBox it's really easy...just make your Egg property on EggBoxViewModel a collection:
public class EggBoxViewModel
{
public string Brand { get; set; }
public int Price { get; set; }
public ICollection<EggViewModel> Eggs { get; set; }
}
Add a second egg:
public ActionResult Demo()
{
EggBoxViewModel eggBox = new EggBoxViewModel();
eggBox.Brand = "HappyEggs";
eggBox.Price = 3;
EggViewModel egg = new EggViewModel();
egg.Size = "Large";
egg.IsBroken = false;
EggViewModel egg2 = new EggViewModel();
egg2.Size = "Medium";
egg2.IsBroken = false;
eggBox.Eggs = new List<EggViewModel>();
eggBox.Eggs.Add(egg);
eggBox.Eggs.Add(egg2);
return View(eggBox);
}
Change your View to render x.Eggs instead of x.Egg:
<p>
#Html.EditorFor(x => x.Eggs)
</p>
Then on post back you'll see there are 2 Eggs posted back:
The field names have automatically been indexed and named to create a collection of Eggs:
you'll have to change your partial view model to EggBoxViewModel so that the Egg object is exposed on the view. i.e:
#model MvcApplication1.ViewModels.EggBoxViewModel
<h2>
Egg</h2>
<p>
#Html.LabelFor(model => model.Egg.Size)
#Html.EditorFor(model => model.Egg.Size)
</p>
<p>
#Html.LabelFor(model => model.Egg.IsBroken)
#Html.CheckBoxFor(model => model.Egg.IsBroken)
</p>
thus, you'd call it from the main view as:
#{Html.RenderPartial("_Egg", Model);}
you should now see the Egg object in your model being returned in the HttpPost action.
[Update method 2]
ok, really quick edit!! You can keep everything the same as initially and try this in your partial (i'm not 100% certain that this will work tho):
#model MvcApplication1.ViewModels.EggViewModel
<h2>
Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.Editor("Egg.Size", Model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBox("Egg.IsBroken", Model.IsBroken)
</p>
here, I just use #Html.Editor() and input the required model name, rather than #Html.EditorFor(). Ok, gorra dash... train to 'cache' ;-)

custom validator in asp.net mvc3

I have created a custom validator in my asp.net mvc3 application like this:
{
if (customerToValidate.FirstName == customerToValidate.LastName)
return new ValidationResult("First Name and Last Name can not be same.");
return ValidationResult.Success;
}
public static ValidationResult ValidateFirstName(string firstName, ValidationContext context)
{
if (firstName == "Nadeem")
{
return new ValidationResult("First Name can not be Nadeem", new List<string> { "FirstName" });
}
return ValidationResult.Success;
}
and I have decorated my model like this:
[CustomValidation(typeof(CustomerValidator), "ValidateCustomer")]
public class Customer
{
public int Id { get; set; }
[CustomValidation(typeof(CustomerValidator), "ValidateFirstName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
my view is like this:
#model CustomvalidatorSample.Models.Customer
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
#using (#Html.BeginForm())
{
#Html.ValidationSummary(false)
<div class="editor-label">
#Html.LabelFor(model => model.FirstName, "First Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName, "Last Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
</div>
<div>
<input type="submit" value="Validate" />
</div>
}
But validation doesn't fire. Please suggest solution.
Thanks
How do you know the validation doesn't fire? Are you setting a break point in your controller?
You are not displaying any validation errors in your view. You need to add the following lines to the view.
#Html.ValidationMessageFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.LastName)
You will want to remove the custom validation from the class. Leave it on the properties though.

Resources