asp.Net MVC view model is empty on post - asp.net

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'

Related

Dropdown Data Binding Problem in ASP.NET Core 6 MVC

I am using SelectListItem in the controller for binding my dropdown data. All the dropdown options are showing perfectly in the dropdown list, but when I try to save, the problem occurs. It's not adding the dropdown options data rather than its adding dropdown data's id.
All the related models, controller and views are shown here:
BuyerSelectList model class:
public class BuyerSelectList
{
[Key]
public int Id { get; set; }
[DisplayName("BUYER")]
public string Buyer { get; set; }
}
ItemSelectList model class:
public class ItemSelectList
{
[Key]
public int Id { get; set; }
[DisplayName("ITEM")]
public string Item { get; set; }
}
BTBNewLien2 model class:
public class BTBNewLien2
{
public int Id { get; set; }
[Required]
[DisplayName("Buyer")]
public int BuyerSelectListId { get; set; }
[ForeignKey("BuyerSelectListId")]
[ValidateNever]
public BuyerSelectList BuyerSelectList { get; set; }
[Required]
[DisplayName("Item")]
public int ItemSelectListId { get; set; }
[ForeignKey("ItemSelectListId")]
[ValidateNever]
public ItemSelectList ItemSelectList { get; set; }
}
BTBNewLien2 controller (here I added all the data binding functionalities for my dropdown):
namespace CommercialCalculatorWeb.Areas.Admin.Controllers
{
public class BTBNewLien2Controller : Controller
{
private readonly IUnitOfWork _unitOfWork;
public BTBNewLien2Controller(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IActionResult Index()
{
IEnumerable<BTBNewLien2> objBTBNewLienList = _unitOfWork.BTBNewLien2.GetAll();
return View(objBTBNewLienList);
}
public IActionResult Create()
{
BTBNewLien2 btbNewLien2 = new();
IEnumerable<SelectListItem> BuyerSelectList = _unitOfWork.Buyer.GetAll().Select(
c => new SelectListItem
{
Text = c.Buyer,
Value = c.Id.ToString()
});
IEnumerable<SelectListItem> ItemSelectList = _unitOfWork.Item.GetAll().Select(
c => new SelectListItem
{
Text = c.Item,
Value = c.Id.ToString()
});
ViewBag.BuyerSelectList = BuyerSelectList;
ViewBag.ItemSelectList = ItemSelectList;
return View(btbNewLien2);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(BTBNewLien2 obj)
{
if (ModelState.IsValid)
{
_unitOfWork.BTBNewLien2.Add(obj);
_unitOfWork.Save();
TempData["success"] = "Row Created Successfully!";
return RedirectToAction("Index");
}
return View(obj);
}
}
}
BTBNewLien2 create view:
#model CommercialCalculator.Models.BTBNewLien2
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BTBNewLien2</h4>
<hr />
<div class="row ml-6">
<div class="col-md-4">
<form asp-action="Create">
<div class="form-group">
<label asp-for="BuyerSelectListId" class="control-label">Buyer</label>
<select asp-for="BuyerSelectListId" asp-items="ViewBag.BuyerSelectList" class="form-control">
<option disabled selected>--Select Buyer--</option>
</select>
</div>
<div class="form-group">
<label asp-for="ItemSelectListId" class="control-label">Item</label>
<select asp-for="ItemSelectListId" asp-items="ViewBag.ItemSelectList" class="form-control">
<option disabled selected>--Select Item--</option>
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
BTBNewLien2 index view:
#model IEnumerable<CommercialCalculator.Models.BTBNewLien2>
#{
ViewData["Title"] = "Index";
}
<table class="table table-bordered table-hover table-sm align-middle m-0" id="header">
<tr class="m-0" style="text-align:center;background-color: #17A2B8">
<th width="20%">
#Html.DisplayNameFor(model => model.BuyerSelectList)
</th>
<th>
#Html.DisplayNameFor(model => model.ItemSelectList)
</th>
</tr>
#foreach (var BTBNewLien2 in Model)
{
<tr class="m-0">
<td>
#Html.DisplayFor(modelItem => BTBNewLien2.BuyerSelectList)
</td>
<td>
#Html.DisplayFor(modelItem => BTBNewLien2.ItemSelectList)
</td>
</tr>
}
</table>
Try this way:
#Html.DropDownList("ItemSelectListId", new SelectList(ViewBag.ItemSelectListId, "Text", "Text"), "-- Select Item --", new { required = true, #class = "form-control" })
In my code, it works fine:
Controller:
[HttpGet]
public IActionResult Create()
{
List<SelectListItem> test = new()
{
new SelectListItem { Value = "1", Text = "test1" },
new SelectListItem { Value = "2", Text = "test2" },
new SelectListItem { Value = "3", Text = "test3" },
new SelectListItem { Value = "4", Text = "test4" }
};
ViewBag.ItemSelectListId = test;
return View();
}
[HttpPost]
public IActionResult Create(Test test)
{
return View();
}
View:
<div class="row ml-6">
<div class="col-md-4">
<form asp-action="Create">
<div class="form-group">
<label asp-for="ItemSelectListId" class="control-label">Buyer</label>
#Html.DropDownList("ItemSelectListId", new SelectList(ViewBag.ItemSelectListId, "Text", "Text"), "-- Select Item --", new { required = true, #class = "form-control" })
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
Test Result:

ViewModel array shows empty value (ASP.Net MVC 5)

I am using ASP.Net MVC, I am trying to get values using a ViewModel.
FAQ class have (Name,Description)
FAQ_Detail class is List of Question and Answer which I am passing as PartialView
I have attached front end page and controller page, in controller I can get the Name,Description but always return NULL FAQ_Detail property
View Model
public class FAQViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IList<Details> FAQ_Details { get; set; }
}
public class Details
{
public int Id { get; set; }
public int FAQId { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FAQ</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<br />
<br />
<br />
#for (int i = 0; i <= 2; i++)
{
var m = new FAQ.Models.ViewModels.Details()
{
Id = i,
FAQId = 11,
Question = string.Format("Quesiton {0}",i),
Answer = string.Format("Ans. {0}",i)
};
#Html.Partial("_AddFAQ",m)
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Controller
[HttpPost]
public ActionResult Create(Models.ViewModels.FAQViewModel model) //The model.FAQ_Details array always empty
{
try
{
//
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The problem is you are just displaying the question/answer pair as form fields and there is nothing to bind them to the array in the model you expect. Usually, the name HTML attribute maps the client-side field to the property name of the model on the server.
The better thing you can do is to have some JavaScript, collect the data and pass that through an AJAX call to the server.
I found a solution, I pass master model in controller and use Detail model as collection
public ActionResult Create(string name,string description, ICollection<Models.ViewModels.Details> data)
{
try
{
//
return RedirectToAction("Index");
}
catch
{
return View();
}
}

Updating Master-Detail / Parent-Child data in MVC

Have a view with a master-detail style view for an invoice. Master is invoice, detail is invoice lines. I'm trying to get detail data items to save on upon an edit post but the detail data is lost upon reaching the post Edit on the controller. So the master data saves fine but the detail is obviously not saved.
Invoice Class:
public class Invoice
{
public Invoice()
{
}
[Required]
[Key]
public int InvoiceID { get; set; }
[Required]
[StringLength(30)]
[DisplayName("Invoice Number")]
public string InvoiceNumber { get; set; }
[Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DataType(DataType.Date)]
[Column(TypeName = "Date")]
[DisplayName("Invoice Date")]
public DateTime InvoiceDate { get; set; }
public List<InvoiceLine> InvoiceLines { get; set; }
[ForeignKey("Client")]
public int OwnerClientIDFK { get; set; }
[DisplayName("Client")]
public Client Client { get; set; }
}
Invoice Line class:
public class InvoiceLine
{
public InvoiceLine()
{
}
[Key]
[Required]
public int InvoiceLineId { get; set; }
[Required]
[StringLength(255)]
[DisplayName("Item")]
public string ItemName { get; set; }
[DisplayName("Description")]
public string ItemDescription { get; set; }
[Required]
public int Quantity { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = true)]
public decimal Value { get; set; }
[ForeignKey("ParentInvoice")]
public int InvoiceID { get; set; }
public Invoice ParentInvoice { get; set; }
}
Controller Edit (get):
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// Invoice invoice = db.Invoices.Find(id);
Invoice invoice = db.Invoices.Include(i => i.InvoiceLines)
.Include(i => i.Client)
.Where(c => c.InvoiceID == id).FirstOrDefault();
if (invoice == null)
{
return HttpNotFound();
}
ViewBag.OwnerClientIDFK = new SelectList(db.Clients, "ClientId", "CompanyName", invoice.OwnerClientIDFK);
return View(invoice);
}
Controller Edit (post):
public ActionResult Edit([Bind(Include = "InvoiceID,InvoiceNumber,InvoiceDate,OwnerClientIDFK")] Invoice invoice)
{
if (ModelState.IsValid)
{
db.Entry(invoice).State = EntityState.Modified;
foreach (var invLine in invoice.InvoiceLines)
{
db.Entry(invLine).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.OwnerClientIDFK = new SelectList(db.Clients, "ClientId", "CompanyName", invoice.OwnerClientIDFK);
return View(invoice);
}
So in the above, when it reaches the foreach, it throws an exception because InvoiceLines is null.
View:
#model DemoApp.Entities.Invoice
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Invoice</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.InvoiceID)
<div class="form-group">
#Html.LabelFor(model => model.InvoiceNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.InvoiceNumber, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.InvoiceNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.InvoiceDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.InvoiceDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.InvoiceDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.OwnerClientIDFK, "OwnerClientIDFK", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("OwnerClientIDFK", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.OwnerClientIDFK, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<h2>Invoice Lines</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-md-8">
<table class="table">
<thead>
<tr>
<th>Item</th>
<th>Description</th>
<th>Qty</th>
<th>Unit Value</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.InvoiceLines.Count; i++)
{
<tr>
<td>#Html.EditorFor(x => x.InvoiceLines[i].ItemName, new { htmlAttributes = new { #class = "form-control" } })</td>
</tr>
}
</tbody>
</table>
</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>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
How do I get it to update the detail / child data?
Any help appreciated. Thanks in advance.
You are not binding InvoiceLines on edit post action method
public ActionResult Edit([Bind(Include = "InvoiceID,InvoiceNumber,InvoiceDate,OwnerClientIDFK,InvoiceLines")] Invoice invoice)
{
What you probably need is to load the Invoice Lines when you get the parent entity.
db.Entry(invoice).Collection(i=>i.InvoiceLines).Load() should do that.
First off, this is kind of a tricky scenario. There are a lot of moving parts. I got really interested in this and wrote a sample application. :)
Here's the link to the repo on GitHub:
https://github.com/benday/asp-mvc-invoice-sample
There are a handful of things going on that are conspiring to cause problems in your code.
1) ironically, the Bind[] that you have in your Edit(Invoice model) method is getting in your way. If you remove it entirely, ASP MVC will attempt to bind everything. Right now, it's only binding what you tell it to bind and since you don't include the InvoiceLines collection, then it's coming up null.
2) if you want ASP MVC to attempt to bind data into your model, you need to POST that data back to the server. If those invoice lines aren't in an html form and aren't represented as form fields, then that data is just going to be missing.
The code you have to draw your invoice lines is missing most of the fields that would be required to populate your InvoiceLine objects.
#Html.EditorFor(x => x.InvoiceLines[i].ItemName, new {
htmlAttributes = new { #class = "form-control" } })
Editor Template for the InvoiceLine class
In my sample code, I created an editor template for the InvoiceLine class. This allows me to easily create bindable html for the InvoiceLines by calling #Html.EditorFor(model => model.Invoice.InvoiceLines) asdf
I know this isn't the simplest answer ever but I hope this helps you over the hump.

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

Resources