I have a simple form with a list of items in it and I'd like to post them to the controller but funny thing is I just cant. Everything else goes through properly except the list. I checked the ajax call in firebug and the post values are there like this:
Answers[0].IsMissing False
Answers[0].Text Ja
Answers[0].Value 0
Answers[1].IsMissing False
Answers[1].Text Nein
Answers[1].Value 1
Id 1cd14b08-ce3b-4671-8cf8-1bcf69f12b2d
Name Ja/Nein
I have an AnwserScheme class with the following properties:
public string Name { get; set; }
public bool IsMissing { get; set; }
public List<AnswerDisplayItem> Answers { get; set; }
public AnswerScheme()
{
Answers = new List<AnswerDisplayItem>();
}
I have this view code:
#for (int i = 0; i < Model.Answers.Count; i++) {
<tr>
<td>
#Html.HiddenFor(model => Model.Answers[i].IsMissing)
#Html.TextBoxFor(model => Model.Answers[i].Value,
new { #class = "inputValue" })
</td>
<td>
#Html.TextBoxFor(model => Model.Answers[i].Text,
new { #class = "inputAnswer" })
</td>
<td>
<span class="span-delete"
data-answer-scheme-id="#Model.Id"
data-answer-id="#Model.Answers[i].Id" >x</span>
</td>
</tr>
}
I have this piece of ajax code that is responsible for posting:
$.ajax({
url: "/AnswerScheme/AddAnswer",
type: "post",
data: $("#formAnswerScheme").serialize(),
success: function (data) {
console.log(data);
$("#divAnswerSchemeContainer").html(data);
}
});
I have an add answer action in my controller:
[HttpPost]
public PartialViewResult AddAnswer(AnswerScheme answerScheme)
{
...some logic comes here
}
So in the end the controller recieves the model, but only the simple properties, not the list. Any help would be greatly appreciated! cheers.
I wish I could see more of your classes and code, because you don't have something set up right.
I recreated something from what you did provide, which works. I created an MVC 3 project for this sample.
Views/Shared/_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
</head>
<body>
#RenderBody()
</body>
</html>
Views/Shared/_Partial.cshtml
#model RazorListTest.Models.AnswerScheme
<table>
#for (int i = 0; i < Model.Answers.Count; i++) {
<tr>
<td>
#Html.HiddenFor(model => Model.Answers[i].IsMissing)
#Html.TextBoxFor(model => Model.Answers[i].Value, new { #class = "inputValue" })
</td>
<td>
#Html.TextBoxFor(model => Model.Answers[i].Text, new { #class = "inputAnswer" })
</td>
<td><span class="span-delete" data-answer-scheme-id="#Model.Id" data-answer-id="#Model.Answers[i].Id" >x</span></td>
</tr>
}
</table>
Models/AnswerDisplayItem.cs
using System.Collections.Generic;
namespace RazorListTest.Models
{
public class AnswerDisplayItem
{
public bool IsMissing { get; set; }
public string Text { get; set; }
public string Value { get; set; }
public string Id { get; set; }
}
public class AnswerScheme
{
public List<AnswerDisplayItem> Answers { get; set; }
public string Id { get; set; }
public AnswerScheme()
{
Answers = new List<AnswerDisplayItem>();
}
}
}
Home/Index.cshtml
#model RazorListTest.Models.AnswerScheme
#using (Html.BeginForm(null, null, FormMethod.Get, new { name="formAnswerScheme", id = "formAnswerScheme"}))
{
{Html.RenderPartial("_Partial");}
<div>
<input type="button" value="Click me" id="btnClick"/>
</div>
<div id="divAnswerSchemeContainer">
</div>
}
<script type="text/javascript">
$("#btnClick").click(function () {
$.ajax({
url: 'Home/AddAnswer',
type: 'POST',
dataType: 'json',
data: $("#formAnswerScheme").serialize(),
success: function (data) {
console.log(data);
$("#divAnswerSchemeContainer").html(data);
},
error: function (xhr, textStatus, exceptionThrown) { alert(JSON.parse(xhr.responseText)); }
});
});
</script>
Controllers/HomeController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using RazorListTest.Models;
namespace RazorListTest.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
AnswerScheme a = new AnswerScheme();
a.Id = "1cd14b08-ce3b-4671-8cf8-1bcf69f12b2d";
List<AnswerDisplayItem> adi = new List<AnswerDisplayItem>();
AnswerDisplayItem a1 = new AnswerDisplayItem();
a1.IsMissing = false;
a1.Text = "Ja";
a1.Value = "0";
a1.Id = "1234";
AnswerDisplayItem a2 = new AnswerDisplayItem();
a2.IsMissing = false;
a2.Text = "Nein";
a2.Value = "1";
a2.Id = "5678";
adi.Add(a1);
adi.Add(a2);
a.Answers = adi;
return View(a);
}
[HttpPost]
public JsonResult AddAnswer(AnswerScheme answerScheme)
{
return Json("the list is in the Model.");
}
}
}
Its almost identical what TheGeekYouNeed posted, but i guess something is just missing. I have no clue what that could be.
AnswerScheme view:
#using System.Web.Mvc.Html
#using MetaDataPortal.Models
#model AnswerScheme
#{
ViewBag.Title = #Model.IsMissing ? "Missing" : "AnswerScheme";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section CssContent{
<link href="../../Content/CSS/AnswerScheme.css" rel="stylesheet" />
}
#using (Html.BeginForm("Save", "AnswerScheme", FormMethod.Post, new { id = "formAnswerScheme" })) {
<div id="divAnswerSchemeContainer">
#{Html.RenderPartial("_AnswerScheme", Model);}
</div>
<input type="button" class="clear inputButton" id="buttonAddCode" value="Add #(Model.IsMissing ? "Missing" : "Answer")" />
<input type="submit" value="Save" />
}
#section Javascript{
<script type="text/javascript">
$(function () {
$("#buttonAddCode").click(function () {
$.ajax({
url: "/AnswerScheme/AddAnswer",
type: "post",
async: false,
data: $("#formAnswerScheme").serialize(),
success: function (data) {
console.log(data);
$("#divAnswerSchemeContainer").html(data);
}
});
return false;
});
});
</script>
<script type="text/javascript" src="~/Content/JavaScript/AnswerScheme.js"></script>
}
_AnswerScheme partialview
#model MetaDataPortal.Models.AnswerScheme
#Html.HiddenFor(model => model.Id, new { Id = "AnswerSchemeId" })
<ul class="ulGeneralForm">
<li>
#Html.LabelFor(model => model.Name, "Name", new { #class = "labelGeneral" })
#Html.TextBoxFor(model => model.Name, Model.Name, new { #class = "textBoxGeneral" })
</li>
<li>
#Html.Label(#Model.IsMissing ? "Missings" : "Answers", new { #class = "labelGeneral" })
<table class="textualData links downloadList">
<thead>
<tr>
<th>Value</th>
<th> #(Model.IsMissing ? "Missing" : "Answer")</th>
<th></th>
</tr>
</thead>
<tbody id="tbodyCodeContainer">
#for (int i = 0; i < Model.Answers.Count; i++) {
<tr>
<td>
#Html.HiddenFor(model => Model.Answers[i].IsMissing)
#Html.TextBoxFor(model => Model.Answers[i].Value, new { #class = "inputValue" })
</td>
<td>
#Html.TextBoxFor(model => Model.Answers[i].Text, new { #class = "inputAnswer" })
</td>
<td><span class="span-delete" data-answer-scheme-id="#Model.Id" data-answer-id="#Model.Answers[i].Id" >x</span></td>
</tr>
}
</tbody>
</table>
</li>
</ul>
AnswerScheme.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using Opit.Rogatus.DomainObjects;
namespace MetaDataPortal.Models
{
public class AnswerScheme : BaseModel
{
public string Name { get; set; }
public bool IsMissing { get; set; }
public List<AnswerDisplayItem> Answers { get; set; }
public AnswerScheme()
{
Answers = new List<AnswerDisplayItem>();
}
public AnswerScheme(CodeList codeList, bool isMissing) : this()
{
Id = codeList.Id;
Name = codeList.Name;
IsMissing = isMissing;
foreach (Code code in codeList.Codes.Where(code => code.Category.IsMissing == isMissing))
{
Answers.Add(new AnswerDisplayItem(code));
}
}
}
}
AnswerDisplayItem.cs:
using System;
using Opit.Rogatus.DomainObjects;
namespace MetaDataPortal.Models
{
public class AnswerDisplayItem
{
public Guid Id { get; private set; }
public short Value { get; private set; }
public string Text { get; private set; }
public Guid AnswerSchemeId { get; set; }
public bool IsMissing { get; private set; }
public AnswerDisplayItem()
{
}
public AnswerDisplayItem(Code code)
{
Id = code.Id;
Value = code.Value;
Text = code.Category.Name;
IsMissing = code.Category.IsMissing;
if (code.CodeList == null) return;
AnswerSchemeId = code.CodeList.Id;
}
}
}
And controller is pretty much the same.
You can do like this create model
public class ApplicationInfo
{
public List<ApplicationAccessRoles> ApplAccessRoleInfo { get; set; }
}
public class ApplicationAccessRoles
{
public int app_access_role_key { get; set; }
public int app_key { get; set; }
public string access_role { get; set; }
public bool inactive { get; set; }
}
put it in view
<div class="step-pane" id="step3">
<div class="form-horizontal" style="vertical-align:central;margin-left:150px">
<table id="RolesDetails" cellpadding="0" cellspacing="0" class="data_table">
<tr class="dataheader">
<td class="width5">
#Html.HiddenFor(m => m.app_access_role_key)
</td>
<td class="width200">
Access Roles Name
</td>
<td class="width10">
Inactive
</td>
</tr>
#if (Model.ApplAccessRoleInfo.Count!= 0)
{
var chk = Model.ApplAccessRoleInfo.Count;
for (int a = 0; a < Model.ApplAccessRoleInfo.Count; a++)
{
<tr class="exp_col_header top_border_nil">
#if ((chk - 1) == a)
{
<td></td>
}
else
{
<td></td>
}
<td>
#Html.HiddenFor(m => m.ApplAccessRoleInfo[a].app_access_role_key)
#Html.EditorFor(m => m.ApplAccessRoleInfo[a].access_role)
</td>
<td>
#Html.CheckBox("ApplTeamAccessInfo[" + a.ToString() + "].inactive", false, new { #class = "check-box"})
</td>
</tr>
}
}
else
{
<tr class="exp_col_header top_border_nil">
<td>
</td>
<td>
#*#Html.EditorFor(model => model.access_role)*#
#*#Html.EditorFor(m => m.ApplAccessRoleInfo[0].access_role)*#
#Html.EditorFor(model=>model.access_role)
</td>
<td>
#Html.CheckBoxFor(model => model.inactive)
</td>
</tr>
}
</table>
</div>
</div>
In Controller
var main = (from a in db.mas_app_access_roles
where a.app_key == AppInfo.app_key
select new ApplicationAccessRoles
{
app_access_role_key = a.app_access_role_key,
access_role = a.access_role,
inactive = a.inactive,
}).ToList();
access = main;
AppInfo.ApplAccessRoleInfo = access;
ViewBag.check = access;
return View(AppInfo);
The problem is with the name/id attribute of your text boxes and other input controls. You can use editor templates to make things seamless and reusable.. Another example here.
But if you still want to loop through things, your loop has to look something in these examples here or here.
Try changing either the controller parameter name to "answers" or the property name to AnswerScheme and if the partial controller in your post is supposed to be recieving the list then change the type to:
List<AnswerScheme> answers
Related
I have PartialView which display pricing information and allow users to enter promocode. when clicking the button on _ListItem.cshtml on the first time, the HttpPost method manage to grab the OrderDetailsViewModel.Details and process in the controller. on the second click of the button, it couldn't capture the data in the Model.
Here are my codes.
NewPurchaseViewModel
public class NewPurchaseViewModel{
public NewPurchaseViewModel()
{
Details = new OrderDetailsViewModel();
Shipping = new Address();
Billing = new Address();
}
public string Email { get; set; }
public string Name { get; set; }
...
public OrderDetailsViewModel Details { get; set; }
}
OrderDetailsViewModel
public class OrderDetailsViewModel{
public OrderDetailsViewModel()
{
Items = new List<ItemsOrderViewModel>();
Shipping = 0.00M;
}
public string Promocode { get; set; }
public string SubTotal{ get; set; }
public string Total{ get; set; }
public string Discount{ get; set; }
public string Shipping{ get; set; }
public List<ItemsOrderViewModel> Items { get; set; }
}
Info.cshtml
#model ~.ViewModels.NewPurchaseViewModel
...
#Html.Partial("~/Views/CheckOut/_ListItem.cshtml", Model.Details, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Details"} })
_ListItem.cshtml
#model Nutric.hive.eStore.ViewModels.OrderDetailsViewModel
#using (Ajax.BeginForm("Verify", "PromoCode", null, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "divPromoCode",
InsertionMode = InsertionMode.Replace
}))
{
<table>
...
<tbody>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>#Model.Items[i].ProductPackage.Name</td>
<td class="qty">#Model.Items[i].Quantity</td>
<td class="price">#Model.Items[i].ItemTotal</td>
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Id)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Name)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Description)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.ImageURL)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Status)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.RetailPrice)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.TaxCode)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.PackageCode)
#Html.HiddenFor(Model => Model.Items[i].Quantity)
#Html.HiddenFor(Model => Model.Items[i].ItemTotal)
</tr>
}
...
<tr>
<td colspan="3">
<div class="col-lg-10">
<div class="input-group">
#Html.TextBoxFor(Model => Model.Promocode, new { #class = "form-control", maxlength = 15, placeholder = "Type Voucher Code Here...", type = "text" })
<span class="input-group-btn">
<input type="submit" class="btn btn-secondary" value="Apply !">
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
#Html.HiddenFor(Model => Model.Discount)
#Html.HiddenFor(Model => Model.Shipping)
#Html.HiddenFor(Model => Model.Subtotal)
#Html.HiddenFor(Model => Model.Total)
}
#section Script{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jquery")
}
PromoCodeController.cs
[HttpPost]
public ActionResult Verify(NewPurchaseViewModel postModel)
{
OrderDetailsViewModel odvm = postModel.Details;
//some logic here to check the promo code.
return PartialView("~/Views/CheckOut/_ListItem.cshtml",odvm );
}
I am trying to get my partial view to display an object name as it iterates through a collection. This is no problem when I refresh the original page but as soon as I do an AJAX post and try to return the new object to the partial view, it cannot find a specific object.
I get a null object error on the FeeTypes object which should be included in the members object as Members.Fees(whateverno).FeeTypesG
Also the ajax post does not refresh the div on success because it never gets success back (or im doing it wrong) NOTE: It still posts everything correctly to the database and saves fine.
Models
public class Members
{
[Key]
public int MemberNo { get; set; }
[Display(Name = "Member Fees")]
public virtual ICollection<Fees> Fees { get; set; }
}
public class Fees
{
[Key]
public int FeeNo { get; set; }
public virtual FeeTypes FeeType { get; set; }
[Display(Name = "Fee Type")]
public int FeeTypesId { get; set; }
[Display(Name = "Frequency of Fee")]
public FeeOccurence FeeOccurence { get; set; }
[Display(Name = "Fee Amount")]
public float FeeAmount { get; set; }
[Display(Name = "Fee Status")]
public FeeStatus FeeStatus { get; set; }
[Display(Name = "Date received (for billing)")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? FeeDate { get; set; }
public virtual Members Members { get; set; }
public int MemberNo { get; set; }
}
public class FeeTypes
{
public int Id { get; set; }
[Display(Name = "Fee Name")]
public string FeeTypeName { get; set; }
}
Controller Action
public ActionResult AddFees(Fees fees)
{
string result;
Members members = db.Members.Find(fees.MemberNo);
ViewBag.Fees = db.Fees
.Include(x => x.FeeType)
.Where(x => x.Members.MemberNo.Equals(x.MemberNo));
ViewBag.FeeTypesId = new SelectList(db.FeeTypes, "Id", "FeeTypeName");
if (ModelState.IsValid)
{
db.Fees.Add(fees);
db.SaveChanges();
return PartialView("~/Views/Members/_MemberDetails.cshtml", members);
}
ViewBag.MemberNo = new SelectList(db.Members, "MemberNo", "FirstName", fees.MemberNo);
result = "Something went wrong!";
return Json(result, JsonRequestBehavior.AllowGet);
}
AJAX Post
saveBtn.click(function () {
var token = $('[name=__RequestVerificationToken]').val();
var postData = {
__RequestVerificationToken: token,
FeeOccurence: feeOccField.val(),
FeeAmount: amountField.val(),
FeeStatus: statusField.val(),
FeeTypesId: typeField.val(),
FeeDate: dateField.val(),
MemberNo: memberNo
};
$.ajax({
url: '/Members/AddFees',
type: 'POST',
data: postData,
success: function (members) {
alert(result);
$("#details").html(members);
},
error: function (result) {
alert(result);
},
traditional: true
});
Partial view
#using AccPortal.Models
#model AccPortal.Models.Members
<h5 class="md-fees-title">Fees</h5>
<table cellpadding="0" cellspacing="0" class="md-fees-table">
<thead>
<tr>
<th>#Html.LabelFor(model => model.Fees.First().FeeDate)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeOccurence)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeTypesId)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeStatus)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeAmount)</th>
<th></th>
</tr>
</thead>
<tbody>
#{ int i = 0; }
#foreach (var item in Model.Fees)
{
<tr data-id="#i">
#Html.HiddenFor(model => model.Fees.ElementAt(i).FeeNo, new { #class = "md-fee-value", name = "md-fee-id", data_type = "testid", id = "testid" })
<td class="md-fee-value" data-type="date">#String.Format("{0:d}", item.FeeDate)</td>
<td class="md-fee-value" data-type="occ">#item.FeeOccurence.GetDisplayName()</td>
//ERROR HAPPENS ON THE NEXT LINE WHEN IT CANT FIND FEETYPES
<td class="md-fee-value" data-type="type">#item.FeeType.FeeTypeName</td>
<td class="md-fee-value" data-type="status">#item.FeeStatus</td>
<td class="md-fee-value" data-type="amount">#item.FeeAmount</td>
<td>
<button type="button" class="md-button md-button--edit" title="edit">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</button>
<button type="button" class="md-button md-button--remove" title="remove">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button>
</td>
</tr>
i++;
}
</tbody>
Top level view
#using AccPortal.Models
#using Newtonsoft.Json
#model AccPortal.Models.Members
#{
ViewBag.Title = "Details";
#Styles.Render("~/Content/jqueryui")
}
<h2>Details</h2>
<div>
<h4>Members Details</h4>
<hr />
</div>
<div class="row">
<div class="col-md-7 md-fees" id ="details">
#Html.Partial("_MemberDetails")
</div>
</div>
The problem was with lazy loading in the end.
I was able to get the Fees to repopulate the bound Members.Fees.FeeType by adding
members = db.Members.Find(fees.MemberNo);
members.Fees = db.Fees.Include(x => x.FeeType).Where(x => x.Members.MemberNo.Equals(x.MemberNo)).ToList();
C#, .Net 4.5, MVC 5
Referring to XX1 and XX2 below (Main View):
The object is initialized in the main controller. (Both the header and the Detial.)
I have added breakpoints at both XX1 and XX2 to confirm that that the initialized values are still there.
No problem with XX1. The initialized values are there and it is
passed to and received by the controller.
XX2 have a problem. The initialized values are still there and is
passed to, but a null object is somehow received by, the controller.
Why do the controller for the detail not pick up the passed parameters for it.
Model:
public class SampleNonRoutine
{
public HeaderNonRoutine SampleHeader { get; set; }
public List<DetailNonRoutine> SampleDetail { get; set; }
public string Comments { get; set; }
}
public class HeaderNonRoutine
{
public string Division { get; set; }
public string Name { get; set; }
public string Telephone { get; set; }
[DisplayName("Sample Title")]
public string SampleTitle { get; set; }
[DisplayName("Retain Sample for")]
public int RetainSample { get; set; }
}
public class DetailNonRoutine
{
public int ID { get; set; }
[DisplayName("Sample Reference #")]
public string SampleReference { get; set; }
[DisplayName("Sample Test")]
public string SamplesTested { get; set; }
[DisplayName("Sample Assays")]
public string SampleAssays { get; set; }
}
Controller:
For Parent View
public ActionResult NonRoutineSamples(string SaveSend)
{
SampleNonRoutine sample = new SampleNonRoutine();
sample.SampleHeader = new HeaderNonRoutine();
sample.SampleDetail = new List<DetailNonRoutine>();
sample.Comments = "Toets";
for (int i = 0; i < 10; i++)
{
sample.SampleDetail.Add(new DetailNonRoutine { ID = i + 1, SampleReference = "", SamplesTested = "", SampleAssays = "" });
}
return View(sample);
}
For Partial Views:
Header (Partial View):
public ActionResult _HeaderNonRoutineSamples(HeaderNonRoutine model)
{
//...some code
PersonDetail pDetail = _db.ListPerson(_MePerson.First().Number.ToString());
model.Name = pDetail.Name + " " + pDetail.Surname;
model.Telephone = pDetail.PhoneWork;
model.Division = pDetail.Division;
model.RetainSample = 30;
return PartialView(model);
}
Detail:
public ActionResult _DetailNonRoutineSamples(List<DetailNonRoutine> model)
{
return PartialView(model);
}
Views:
Main
#model LaboratorySampleRegistration.Models.SampleNonRoutine
#{
ViewBag.Title = "NonRoutineSamples";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h4>Non-Routine Samples</h4>
<table>
<tr>
<td>
#Html.Action("_HeaderNonRoutineSamples", "Home", Model.SampleHeader) XX1
</td>
</tr>
<tr>
<td>
#Html.Action("_DetailNonRoutineSamples", "Home", Model.SampleDetail) XX2
</td>
</tr>
<tr>
<td>
<div>
#Html.LabelFor(model => model.Comments, htmlAttributes: new { #class = "control-label col-md-2" })
<div>
<span>
#Html.TextAreaFor(model => model.Comments, new { #class = "SampleRoutineComments" })
#Html.ValidationMessageFor(model => model.Comments, "", new { #class = "text-danger" })
</span>
</div>
</div>
</td>
</tr>
<tr>
<td>
<hr style="border-top:1px solid black !important;" />
<p>
<input id="SaveSend" name="SaveSend" type="submit" value="Send to Laboratory" class="btn btn-default" />
</p>
</td>
</tr>
</table>
}
I need to submit list of skills. when i try to do this submitted model is null. But in Fidler I see that data was send.
public class MvcSkill
{
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Display(Name = "Category Name")]
[StringLength(128, ErrorMessage = "Max length - {0} symbols")]
[Required(ErrorMessage = "Please enter your last name")]
public string Name { get; set; }
public int Level { get; set; }
[Required(ErrorMessage ="Choose skill category")]
public string CategoryName { get; set; }
}
Partial view for each skill (strange input for skill level is bootstrap-star-rating:
#using MvcApp.Infrastructure;
#model MvcApp.ViewModels.MvcSkill
<tr>
<td>
#Html.HiddenFor(x => x.Id)
</td>
<td>
#Html.HiddenFor(x => x.Name)
#Html.DisplayFor(x => x.Name)
</td>
<td>
#Html.HiddenFor(x => x.CategoryName)
#Html.DisplayFor(x => x.CategoryName)
</td>
<td>
<input for="Level" id="#Model.Id" value="#Model.Level" name="Level" type="number" class="rating-loading" data-size="xs" data-min="0" data-max="5" data-step="1" data-show-clear="false">
</td>
#{
var identity = (CustomIdentity)User.Identity;
if (identity.Roles.FirstOrDefault(r => r == "Administrator") != null)
{
<td>
#Html.RouteLink("Edit", new { controller = "Administrator", action = "EditSkill", id = Model.Id })
</td>
<td>
#Html.RouteLink("Remove", new { controller = "Administrator", action = "RemoveSkill", id = Model.Id })
</td>
}
}
</tr>
View for list of skills:
#model IList<MvcApp.ViewModels.MvcSkill>
#{
ViewBag.Title = "Skills";
}
#using (Html.BeginForm("Index","Skill", Model))
{
<table>
#foreach (var s in Model)
{
#Html.Partial("_Skill", s)
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="submit" type="submit" value="Update" class="btn btn-default" />
</div>
</div>
}
#section scripts{
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" rel="stylesheet">
<link href="~/Content/star-rating.css" media="all" rel="stylesheet" type="text/css" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.js"></script>
<script src="~/Scripts/star-rating.js" type="text/javascript"></script>
#foreach (var item in Model)
{
string id = item.Id.ToString();
<script>
$(document).on('ready', function () {
$('##id').rating({
step: 1,
defaultCaption: 'Unknown',
starCaptions: { 0: 'Unknown', 1: 'Beginner', 2: 'Elementary', 3: 'Intermediate', 4: 'Advanced', 5: 'Profi' },
starCaptionClasses: function (val) { return 'text-success'; }
}).on('rating.change', function (event, value, caption) {
Model.Skills.FirstOrDefault(s => s.Key.Id.ToString() == id).Value = value;
});
});
</script>
}
}
And controller method:
[HttpPost]
public ActionResult Index(List<MvcSkill> skillModel)
{
//Do something...
return View();
}
Fidler text:
Id=1&Name=c%2B%2B&CategoryName=Programing&Level=5&Id=3&Name=PHP&CategoryName=Programing&Level=3&Id=6&Name=JAVA&CategoryName=Programing&Level=3&Id=7&Name=Name&CategoryName=.Net+Frameworks&Level=5
You have to manually add indexes so MVC framework can successfully bind data. Something like:
#using (Html.BeginForm("Index","Skill", Model))
{
<table>
#for (int i = 0; i < Model.Length; i++)
{
<tr>
<td>
#Html.Hidden("[" + i + "].Id");
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Name)
#Html.Hidden("[" + i + "].Name");
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).CategoryName)
#Html.Hidden("[" + i + "].CategoryName");
</td>
</tr>
}
</table>
}
Or, as stated in comments you can use BeginCollectionItem (which I personally use). What BeginCollectionItem does is, generally it adds GUIDs as index fro you. Source code can be found here and you can get familiar with it here.
The below code displays list of countries with checkbox for each. The intend is to save which checkbox was checked.
But when the submit button is clicked, in the method
ActionResult Index(UserModel newmodel) in the newmodel, the value of parameters SelectedSecurityGroup, SelectedSecurityObject and SecurityGroups is null.
Any idea what is wrong with this code?
In UserModel.cs
public class UserModel
{
public string SelectedSecurityGroup { get; set; }
public string SelectedSecurityObject { get; set; }
[DisplayName("Security Group")]
public virtual ICollection<SecurityGroup> SecurityGroups { get; set; }
}
public class SecurityGroup
{
public int Id { get; set; }
public string SecurityGroupName { get; set; }
public bool Active { get; set; }
}
In UserController.cs
[HttpGet]
public ActionResult Index()
{
UserModel objUserModel = new UserModel();
List<SecurityGroup> lstSecurityGroup = FillViewBag();
objUserModel.SecurityGroups = lstSecurityGroup;
return View(objUserModel);
}
[HttpPost]
public ActionResult Index(UserModel newmodel)
{
string strtest = "";
//Code to save data
return View(newmodel);
}
private List<SecurityGroup> FillViewBag(UserModel model = null)
{
List<SecurityGroup> lstSecurityGroup = new List<SecurityGroup>();
lstSecurityGroup.Add(new SecurityGroup { Id = 1, SecurityGroupName = "India", Active = true });
lstSecurityGroup.Add(new SecurityGroup { Id = 2, SecurityGroupName = "USA", Active = true });
lstSecurityGroup.Add(new SecurityGroup { Id = 3, SecurityGroupName = "Pakistan", Active = false });
lstSecurityGroup.Add(new SecurityGroup { Id = 4, SecurityGroupName = "Nepal", Active = false });
return lstSecurityGroup;
}
In Index.cshtml
#model Example.User.Web.Models.UserModel
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<script src="~/Scripts/jquery-1.11.0.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
</head>
<body>
<div>
<div>
<div id="lists">
#Html.Partial("SecurityListsView", Model)
</div>
</div>
</div>
</body>
</html>
In SecurityListsView.cshtml
#model Example.User.Web.Models.UserModel
#using (Ajax.BeginForm("Index", "User", new AjaxOptions() { UpdateTargetId = "lists" }))
{
<table>
#{ int i = 0; }
#foreach (var item in Model.SecurityGroups )
{
<tr>
<td>
#Html.CheckBox("fileName", item.Active)
#Html.Hidden("fileId", item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.SecurityGroupName)
</tr>
i++;
}
</table>
<input type="submit" name="btn1" value="Save" />
}
Finally got it working. Below is the corrected code:
In SecurityListsView.cshtml
#model Example.User.Web.Models.UserModel
#using (Html.BeginForm("Index", "User", "POST"))
{
<table>
#{ int i = 0; }
#foreach (var newitem in Model.SecurityGroups)
{
<tr>
<td>
#Html.CheckBoxFor(model => model.SecurityGroups[i].Active)
#Html.HiddenFor(model => model.SecurityGroups[i].Id, "Value")
</td>
<td>
#Html.DisplayFor(model => model.SecurityGroups[i].SecurityGroupName)
</tr>
i++;
}
</table>
<input type="submit" name="btn1" value="Save" />
}
Hope it helps someone! :)