Date validation on search input/textbox for a razor view MVC - asp.net-mvc-3

I would like to add a textbox (date) and button to my report, which filters the data.
The below mvc is working, but the input must still be validated (must be a DATE) on client side (and server side if possible)
My Model looks like this :
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
View :
#model IEnumerable<project_name.Models.DailyReport>
#* text box and button: *#
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{ <p>
Title: #Html.TextBox("SearchDateString")
<input type="submit" value="Filter" />
</p>
}
#* display dates*#
#foreach (var item in Model)
{ #Html.DisplayFor(modelItem => item.ReportDate)
}
my controller:
public ViewResult Index(String SearchDateString)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (!String.IsNullOrEmpty(SearchDateString))
{
DateTime search_date = Convert.ToDateTime(SearchDateString);
dailyreport = dailyreport.Where(r => r.ReportDate == search_date);
}
return View(dailyreport.ToList());
}
Can someone help me please?
How do I make sure a valid date is entered in the textbox?
Should I create a another model with a date field for this input?

Utilize the DataTypeAttribute from the DataAnnotations namespace in your Model, like so:
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
public class DrViewModel
{
[DataType(DataType.Date)]
public string DateTimeSearch { get; set; }
List<DailyReport> DailyReports { get; set; }
}
In your View, have something like:
#model project_name.Models.DrViewModel
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{
<p>
Title: #Html.TextBoxFor(m => m.DateTimeSearch)
<input type="submit" value="Filter" />
</p>
}
#foreach (var item in Model.DailyReports)
{
#Html.DisplayFor(m => item.ReportDate)
}

#Shark Shark pointed me in the right direction to use a viewmodel, this is the end result that is now working. JS validation added as well.
(DBSet was not necessary because DrViewModel is a viewmodel.)
controllers :
public ActionResult Index(DrViewModel dvm)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (dvm.DateTimeSearch != null)
{
dailyreport = dailyreport.Where(r => r.ReportDate == dvm.DateTimeSearch);
}
dvm.DailyReport = dailyreport.ToList();
return View(dvm);
}
models :
public class DrViewModel
{
public DateTime? DateTimeSearch { get; set; }
public List<DailyReport> DailyReport { get; set; }
}
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
view :
#model myproject.Models.DrViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<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("Index","DailyReport", FormMethod.Get ))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.DateTimeSearch)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateTimeSearch)
#Html.ValidationMessageFor(model => model.DateTimeSearch)
<input type="submit" value="Filter" />
</div>
}
#foreach (var item in Model.DailyReport)
{
#Html.DisplayFor(modelItem => item.ReportDate)
}

Related

How to use CheckBoxFor in MVC forms with other form controls

Basically, i have a form with a textbox, radio button and a check box control. now i face problem with the checkbox control when i submit my page
I have a model like this
public class PersonDetails
{
public int personID { get; set; }
public string PersonName { get; set; }
public string Gender { get; set; }
public List<Education> Education { get; set; }
public string EmailID { get; set; }
public string Address { get; set; }
}
public class Education
{
public string Qualification { get; set; }
public bool Checked { get; set; }
public List<Education> GetQualification()
{
return new List<Education>{
new Education {Qualification="SSC",Checked=false},
new Education {Qualification="HSC",Checked=false},
new Education {Qualification="Graduation",Checked=false},
new Education {Qualification="PostGraduation",Checked=false}
};
}
}
and i have a view like this
#using (Html.BeginForm("GetDetails", "User", FormMethod.Post, new { id = "person-form" }))
{
<div class="col-xs-12">
<label>Person Name</label>
#Html.TextBoxFor(x => x.PersonName)
</div>
<div class="col-xs-12">
<label>Gender</label>
#Html.RadioButtonFor(x => x.Gender, "Male")
#Html.RadioButtonFor(x => x.Gender, "Female")
</div>
<div class="col-xs-12">
<label>Education</label>
#{
Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification());
}
</div>
<div class="col-xs-12">
<input type="submit" value="Submit" />
</div>
}
and the partial view like this
#model List<LearnAuthentication.Controllers.Education>
<br />
#for (int i = 0; i < Model.Count(); i++)
{
#Html.HiddenFor(x => Model[i].Qualification)
#Html.CheckBoxFor(x => Model[i].Checked)
#Html.DisplayFor(x => Model[i].Qualification)
<br />
}
and my action method is this
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails)
{
return View();
}
now when i run my app i tend to get all the information but when i submit the page i get this property with null values
public List Education { get; set; }
can any of you guys help me on what i am doing wrong or could you direct me to the right path on how to achieve this.
Your use of a partial to generate the controls for Education is generating inputs such as
<input type="hidden" name="[0].Qualification" ... />
<input type="hidden" name="[1].Qualification" ... />
but in order to bind, they need to have name attributes which match your model
<input type="hidden" name="Education[0].Qualification" ... />
<input type="hidden" name="Education[1].Qualification" ... />
Rename you partial to Education.cshtml (to match the name of the class) and move it to your /Views/Shared/EditorTemplates folder (or /Views/yourControllerName/EditorTemplates if you want a specific template just for that controller)
Then change the partial to
#model LearnAuthentication.Controllers.Education
#Html.HiddenFor(m => m.Qualification)
#Html.LabelFor(m => m.Checked)
#Html.CheckBoxFor(m => m.Checked)
#Html.DisplayFor(m => m.Qualification)
and in the main view replace
<label>Education</label>
#{ Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification()); }
with
<span>Education</span> // its not a label
#Html.EditorFor(m => m.Education)
which will correctly generate the correct html for each item in your collection
Side note: Other alternatives which would work would be to change the POST method signature to
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails List<Education> educationDetails)
or to pass the HtmlFieldPrefix to the partial as explained in getting the values from a nested complex object that is passed to a partial view

ASP.NET MVC Ajax file upload with jquery form plugin?

I use Jquery Ajax Form Plugin to upload file. Codes:
AuthorViewModel
public class AuthorViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "{0} alanı boş bırakılmamalıdır!")]
[Display(Name = "Yazar Adı")]
public string Name { get; set; }
[Display(Name = "Kısa Özgeçmiş")]
public string Description { get; set; }
[Display(Name = "E-Posta")]
public string Email { get; set; }
public string OrginalImageUrl { get; set; }
public string SmallImageUrl { get; set; }
}
Form
#using (Html.BeginForm("_AddAuthor", "Authors", FormMethod.Post, new { id = "form_author", enctype = "multipart/form-data" }))
{
<div class="editor-label">
<input type="file" name="file" id="file" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-field">
#Html.ValidationMessageFor(model => model.Name)
</div>
...
<div class="submit-field">
<input type="submit" value="Ekle" class="button_gray" />
</div>
}
Script
<script>
$(function () {
$('#form_author').ajaxForm({
beforeSubmit: ShowRequest,
success: SubmitSuccesful,
error: AjaxError
});
});
function ShowRequest(formData, jqForm, options) {
$(".loading_container img").show();
}
function AjaxError() {
alert("An AJAX error occured.");
}
function SubmitSuccesful(result, statusText) {
// Veritabanı işlemleri başarılı ise Index sayfasına
// geri dön, değilse partial-view sayfasını yenile
if (result.url) {
window.location.href = result.url;
} else {
$(".authors_content_container").html(result);
}
}
</script>
Controller
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel, HttpPostedFileBase file)
{
...
viewModel.OrginalImageUrl = file.FileName;
...
}
Above codes work fine
Question
As you see, I post file seperate from ViewModel. Is there a way to add HttpPostedFileBase file property to ViewModel and bind it to viewModel in view, And post it to controller in ViewModel?
I hope , I can explain.
EDIT:
This codes work fine. I dont want post , viewModel and HttpPostedFile seperately. I want something like this: (If it is possible.)
Model
public class AuthorViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "{0} alanı boş bırakılmamalıdır!")]
[Display(Name = "Yazar Adı")]
HttpPostedFileBase file{ get; set; }
...
}
Controller
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel)
{
var file = viewModel.file;
...
}
Thanks.
Yes you can add AliRıza Adıyahşi.
Here is the property to do it:
public HttpPostedFileBase File { get; set; }
Now in you form you should add enctype as Xiaochuan Ma said:
#using (Html.BeginForm("_AddAuthor", "Authors", FormMethod.Post, new { id = "form_author", enctype="multipart/form-data" }))
{
<div class="editor-label">
<input type="file" name="file" id="file" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-field">
#Html.ValidationMessageFor(model => model.Name)
</div>
...
<div class="submit-field">
<input type="submit" value="Ekle" class="button_gray" />
</div>
}
On you Controller action:
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel, HttpPostedFileBase file)
{
if(file!=null)
{
viewModel.File=file; //Binding your file to viewModel property
}
//Now you can check for model state is valid or not.
if(ModelState.IsValid)
{
//do something
}
else
{
return View(viewModel);
}
}
Hope it helps. Is this what you need ?
EDIT
There is nothing additional to do. Its automatically binding to viewModel.
[HttpPost]
public ActionResult _AddAuthor(AuthorViewModel viewModel)
{
var uploadedfile = viewModel.File;// Here you can get the uploaded file.
//Now you can check for model state is valid or not.
if(ModelState.IsValid)
{
//do something
}
else
{
return View(viewModel);
}
}
This worked for me !
Yes, you can add HttpPostedFileBase in to your ViewModel, and add enctype = "multipart/form-data" to your From in HTML.
Check with this link:
MVC. HttpPostedFileBase is always null

Using Ajax gives stale results

EDIT
My Ajax form gets correct Id to update a content and a replace option.
Submitting is by clicking <input type="submit" value="submit!" />.
Problem: When I clicked on submit I didn't see any update. Second trial gave the expected result. When I refreshed the page the lost record (first hit was successful) was on spot.
Model
[Serializable]
public abstract class AbstractEntity {
public Guid Id { get; set; }
public DateTime LastModified { get; set; }
}
[Serializable]
public class Product : AbstractEntity {
public Product() {
this.Attachments = new HashSet<Attachment>();
}
public String Title { get; set; }
public String Commentary { get; set; }
public DateTime PlacedOn { get; set; }
public String User { get; set; }
public ICollection<Attachment> Attachments { get; set; }
}
[Serializable]
public class Attachment {
public String MimeType { get; set; }
public String Description { get; set; }
public String Filename { get; set; }
}
Controller
[HandleError]
public class ProductController : Controller {
private readonly IDocumentSession documentSession;
public ProductController(IDocumentSession documentSession) {
this.documentSession = documentSession;
}
public ActionResult ListRecent() {
return View(ListAll());
}
[Audit, HttpPost]
public ActionResult Delete(Guid id) {
documentSession.Delete<Product>(documentSession.Load<Product>(id));
documentSession.SaveChanges();
return PartialView("ProductsList", ListAll());
}
[Audit, HttpPost]
public ActionResult Create(Product product) {
if(ModelState.IsValid) {
documentSession.Store(product);
documentSession.SaveChanges();
}
return PartialView("ProductsList", ListAll());
}
private IEnumerable<Product> ListAll() {
return documentSession.Query<Product>().ToArray();
}
}
Views ('scriptless')
Layout
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/stylesheets/normalize.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/stylesheets/site.core.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.7.2.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
</head>
<body>
<div id="main-wrapper">
<div id="header">[#Html.ActionLink("List", "ListRecent", "Product")]</div>
<div id="content">#RenderBody()</div>
</div>
</body>
ListRecent.cshtml
#model IEnumerable<lamp.DomainLayer.Entities.Product>
#{
ViewBag.Title = "ListRecent";
var options = new AjaxOptions {
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "productsList"
};
}
<h2>List</h2>
#Html.Partial("ProductsList", Model)
#using(Ajax.BeginForm("Create", "Product", options)) {
<fieldset>
<p>
<label class="autoWidth">Title</label>
#Html.Editor("Title")
#Html.ValidationMessage("Title")
</p>
<p>
<label class="autoWidth">Commentary</label>
#Html.TextArea("Commentary")
#Html.ValidationMessage("Commentary")
</p>
#* Some fields I have omitted.. *#
<input type="submit" value="submit" />
<input type="reset" value="clear" />
</fieldset>
}
ProductsList.cshtml
#model IEnumerable<lamp.DomainLayer.Entities.Product>
#{
var options = new AjaxOptions {
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "productsList"
};
}
<div id="productsList">
#foreach(var p in Model) {
<div class="productCard">
<p class="title"><strong>Title</strong>: #p.Title</p>
<p class="author"><strong>User</strong>: #p.User</p>
<p class="date"><strong>Placed on</strong>: #idea.PlacedOn.ToShortDateString()</p>
<p class="link">#Html.ActionLink("details", "Details", "Product")</p>
<p class="link">
#using(Ajax.BeginForm("Delete", "Product", new { id = p.Id }, options)) {
<input type="submit" value="you!" />
}
</p>
</div>
}
</div>
I guess it is IE's fault (precisely the way,how IE cache AJAX requests). Look here-it could be solution:
http://viralpatel.net/blogs/ajax-cache-problem-in-ie/
Ok. Darin was right! I found out that changing my controller code to this one:
private IEnumerable<Product> ListAll() {
return documentSession
.Query<Product>()
.Customize((x => x.WaitForNonStaleResults()))
.ToArray();
}
fixes everything.
.Customize((x => x.WaitForNonStaleResults()))
is the solution!
Thanks everyone!

can not add data from dropdown to table

I am using nhibernate and mvc3 in asp.net
I'm trying to add data into table where my table schema is like this:
public class HobbyMasters
{
[Key]
public virtual int HobbyId { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "NameRequired")]
public virtual string HobbyName { get; set; }
public virtual HobbyTypes HobbyType { get; set; }
[Required]
public virtual string HobbyDetails { get; set; }
[Required]
public virtual ICollection<HobbyMasters> HobbyNames { get; set; }
}
public class HobbyTypes
{
[Key]
public virtual int HobbyTypeId { get; set; }
[Required]
public virtual string HobbyType { get; set; }
public virtual ICollection<HobbyTypes> Hobby { get; set; }
}
in my Controller
public ActionResult Create()
{
ViewBag.c1 = (ICollection<HobbyTypes>)(new Hobby_MasterService().GetAllHobbyTypes());
return View();
}
//
// POST: /Hobbies/Create
[HttpPost]
public ActionResult Create(HobbyMasters hobby)
{
ViewBag.c1 = (ICollection<HobbyTypes>)new Hobby_MasterService().GetAllHobbyTypes();
try
{
if (ModelState.IsValid)
{
new Hobby_MasterService().SaveOrUpdateHobby(hobby);
return RedirectToAction("Index");
}
}
}
in the view:
#using (Html.BeginForm("Create", "Hobbies", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Hobby Master</legend>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyName)
#Html.ValidationMessageFor(model => model.HobbyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyType)
</div>
<div class="Editor-field">
#Html.DropDownListFor(model =>model.HobbyType.HobbyTypeId, new SelectList(ViewBag.c1, "HobbyTypeId", "HobbyType"), "-- Select --")
#Html.ValidationMessageFor(model => model.HobbyType)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyDetails)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyDetails)
#Html.ValidationMessageFor(model => model.HobbyDetails)
</div>
</fieldset>
<p><input type="Submit" value="Create" /> </p>
}
Apparently i found that My Modelstate.IsValid is always false.....
since it stores only the HobbyId and getting Hobby Type as null the HobbyMasters hobbytype object.....
dnt knw where i'm going wrong may be in dropdownlist or something else.....
Plaese help me asap:(
There are a couple of issues with your code:
First is the fact that you decorated HobbyNames collection property with the [Required] attribute. You should use this attribute only on simple properties. In fact you could leave the property but it will have absolutely no effect
The second issue with your code is that you have decorated the HobbyType string property of the HobbyTypes model with a [Required] attribute but you never use this property in your view. So no value is sent when you submit the form and your model is invalid.
Another issue with your code is that you bound the dropdown list to the model => model.HobbyType.HobbyTypeId property. But the HobbyTypeId is not a nullable type. And yet you made your dropdown contain a default value: "-- Select --". This is not possible. If you want to have a dropdown list with an optional value you must bind it to a nullable property on your model.
I have tried to clean up your code a little.
Model:
public class HobbyMasters
{
public virtual int HobbyId { get; set; }
[Required]
public virtual string HobbyName { get; set; }
public virtual HobbyTypes HobbyType { get; set; }
[Required]
public virtual string HobbyDetails { get; set; }
public virtual ICollection<HobbyMasters> HobbyNames { get; set; }
}
public class HobbyTypes
{
[Required]
public virtual int? HobbyTypeId { get; set; }
public virtual string HobbyType { get; set; }
public virtual ICollection<HobbyTypes> Hobby { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Create()
{
ViewBag.c1 = (ICollection<HobbyTypes>)(new Hobby_MasterService().GetAllHobbyTypes());
var model = new HobbyMasters();
return View(model);
}
//
// POST: /Hobbies/Create
[HttpPost]
public ActionResult Create(HobbyMasters hobby)
{
if (ModelState.IsValid)
{
try
{
new Hobby_MasterService().SaveOrUpdateHobby(hobby);
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
}
ViewBag.c1 = (ICollection<HobbyTypes>)new Hobby_MasterService().GetAllHobbyTypes();
return View(hobby);
}
}
View:
#model HobbyMasters
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Hobby Master</legend>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyName)
#Html.ValidationMessageFor(model => model.HobbyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyType)
</div>
<div class="Editor-field">
#Html.DropDownListFor(model => model.HobbyType.HobbyTypeId, new SelectList(ViewBag.c1, "HobbyTypeId", "HobbyType"), "-- Select --")
#Html.ValidationMessageFor(model => model.HobbyType.HobbyTypeId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HobbyDetails)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HobbyDetails)
#Html.ValidationMessageFor(model => model.HobbyDetails)
</div>
</fieldset>
<p><input type="Submit" value="Create" /></p>
}
Also I would very strongly recommend you to use view models. Don't pass your domain entities to your views. Define view models.

Checkboxes in editor template are not bound to model in post action

I am using an editor template to display a checkbox for each role a user can be assigned to. The model is:
public class UserModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public IEnumerable<string> UserRoles { get; set; }
}
public class UserRoleModel
{
public IEnumerable<RoleViewModel> AllRoles { get; set; }
public UserModel user { get; set; }
public UserRoleModel()
{
this.AllRoles = Roles.GetAllRoles().Select(r => new RoleViewModel
{
Name = r
});
this.user = new UserModel();
}
}
public class RoleViewModel
{
public string Name { get; set; }
public bool Selected { get; set; }
}
The Controller:
public ActionResult Create()
{
return View(new UserRoleModel());
}
[HttpPost]
public ActionResult Create(UserRoleModel model)
{
if (ModelState.IsValid)
{
MembershipCreateStatus createStatus;
Membership.CreateUser(model.user.UserName, model.user.Password, model.user.Email, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
foreach (var r in model.AllRoles)
{
if (r.Selected)
{
Roles.AddUserToRole(model.user.UserName, r.Name);
}
}
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
return View(model);
}
The view:
#model BBmvc.Areas.Tools.Models.UserRoleModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>UserModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.user.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.user.UserName)
#Html.ValidationMessageFor(model => model.user.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.user.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.user.Email)
#Html.ValidationMessageFor(model => model.user.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.user.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.user.Password)
#Html.ValidationMessageFor(model => model.user.Password)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.user.ConfirmPassword)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.user.ConfirmPassword)
#Html.ValidationMessageFor(model => model.user.ConfirmPassword)
</div>
<div class="editor-field">
#Html.EditorFor(x => x.AllRoles)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
and the editor template
#model BBmvc.Areas.Tools.Models.RoleViewModel
#Html.CheckBoxFor(x => x.Selected)
#Html.LabelFor(x => x.Selected, Model.Name)
#Html.HiddenFor(x => x.Name)
<br />
The problem is that no distinction is made in the post action whether any checkbox is checked or not. It seems that it is not getting bound to the model somehow.
Your issue comes from the deferred execution of LINQ queries. You need to eagerly initialize the collection:
public class UserRoleModel
{
public IEnumerable<RoleViewModel> AllRoles { get; set; }
public UserModel user { get; set; }
public UserRoleModel()
{
this.AllRoles = Roles.GetAllRoles().Select(r => new RoleViewModel
{
Name = r
}).ToList();
this.user = new UserModel();
}
}
Notice the .ToList() call:
this.AllRoles = Roles.GetAllRoles().Select(r => new RoleViewModel
{
Name = r
}).ToList();
And here's the explanation. When you write:
this.AllRoles = Roles.GetAllRoles().Select(r => new RoleViewModel
{
Name = r
});
at this moment the query is not executed. Only an expression tree is built but the actual query is executed only when something starts iterating over the collection. And what starts iterating? First it's the view. Inside the view you use an editor template for this collection:
#Html.EditorFor(x => x.AllRoles)
Since AllRoles is a collection property ASP.NET MVC will automatically iterate and render the editor template for each element of the collection. So this works to properly render the view.
Now let's see what happens when the form is POSTed. You post to the Create action and the default model binder kicks in. The constructor is called but since there is nothing to iterate over the AllRoles property this time the query is not executed. In fact it is executed later inside the action and the values are lost.
For this reason I would recommend you to avoid initializing your view models inside constructors. It would be better to do this inside the respective controller actions:
public class UserRoleModel
{
public IEnumerable<RoleViewModel> AllRoles { get; set; }
public UserModel user { get; set; }
}
and then:
public ActionResult Create()
{
var model = new UserRoleModel
{
AllRoles = Roles.GetAllRoles().Select(r => new RoleViewModel
{
Name = r
}).ToList(),
user = new UserModel()
};
return View(model);
}

Resources