Get checked checkbox MVC5 - asp.net

I have searched some articles but none of them fits with my problem. i have this code in my cshtml
#model IEnumerable<MonitoreoIntegrado.Models.Sensores>
#using (Html.BeginForm("Graphics", "Datos_extensometro", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<div style="margin:0; padding:0; border:none; position:absolute; bottom:5px; right:25px;"><input type="submit" id="enviar" class="btn btn-primary" value="Consultar"></div>
<div style="float:left; width:428px; height:105px; padding:5px; overflow-y: scroll;">
<h4 style="margin-top:0px;">Consultar multiples sensores</h4>
#foreach (var item in Model)
{
#Html.CheckBox("sensoresCB", false, new { value = #item.idSensor.ToString() }) #item.nombre <a class="moverse" objetivo="#(item.idSensor)" href=""><span class="glyphicon glyphicon-map-marker" title="Ir al marcador"></span></a><br />
}
</div>
}
and this in my controller:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = RolesSistema.Administrador + "," + RolesSistema.Lectura)]
public ActionResult Graphics()
{
return View();
}
I need help to receive that checkboxes in my controller and check which of them are checked

You should use unique id's for your inputs. The easiest way:
#for (var i = 0; i < Model.Count(); i++)
{
#Html.CheckBox("sensoresCB["+i.ToString()+"]", false, new { value = Model[i].idSensor.ToString() })
//your other stuff
}
And on controller side:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = RolesSistema.Administrador + "," + RolesSistema.Lectura)]
public ActionResult Graphics(bool[] sensoresCB) //note this
{
return View();
}
So you got the array with your selections. Use index to understand what checkbox it is.

You could extend the model of item with a Checked property or similar.
public class GraphicsViewModel {
public GraphicsItemViewModel[] Items { get; set; }
}
public class GraphicsItemViewModel {
public bool Checked { get; set; }
public long IdSensor { get; set; }
}
Then you can render the checkbox with a binding to this Checked property.
#model GraphicsViewModel
#using (Html.BeginForm(/* ....*/) {
// use a for loop so the array is bound correctly
#for (int i = 0; i < Model.Items.Length; i++) {
#Html.CheckBoxFor(m => m.Items[i].Checked)
// post back IdSensor value so we can access it in the controller
#Html.HiddenFor(m => m.Items[i].IdSensor)
}
}
Your controller should accept a model for the POST data, you can reuse the ViewModel:
[HttpPost]
public ActionResult Graphics(GraphicsViewModel postData) {
bool areAllChecked = postData.Items.All(i => i.Checked);
bool isFirstChecked = postData.Items.First().Checked;
bool isCertainIdChecked = postData.Items.Single(i => i.IdSensor == 1337).Checked;
// ...
}

I would like to mention a helpful point here, that is
If a checkbox is checked, then the postback values will contain a
key-value pair of the form [InputName]=[InputValue]
If a checkbox is not checked, then the posted form contains no
reference to the checkbox at all.
So in a controller action method, you can use the name of the checkbox and get the values which is only checked
ex:
public ActionResult Graphics(bool[] sensoresCB)
{
return View();
}
Hope above information was helpful
Thanks
Karthik

Based on #teo van kot reply I achieved to recover the checkboxes values. This is my working code.
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = RolesSistema.Administrador + "," + RolesSistema.Lectura)]
public ActionResult Graphics(int[] sensoresCB)//you can receive a string[] as well
{
//code and stuff
return View();
}
View:
#model IEnumerable<MonitoreoIntegrado.Models.Sensores>
#using (Html.BeginForm("Graphics", "Datos_extensometro", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
//some code stuff
#{
int i = 0;
foreach (var item in Model)
{
#Html.CheckBox("sensoresCB[" + i.ToString() + "]", false, new { value = item.idSensor.ToString() }) #item.nombre<br />
i++;
}
}
}

Related

Incorrect values being posted from View after dynamically removing rows

I have the option of dynamically adding and removing rows in my form. Before submitting the form if I remove the last row it works fine but if I remove any other row not in a sequence then add a row and then submit the form it posts the wrong value in my model (i.e. it changes the data of the new row to the data of previous row)
I am using hidden fields but that did not help. The main view renders a partial view which contains the data for the rows. I have tried to use EditorFor but that didn't help either.
Main View:
#using (Html.BeginForm("Submit", "TimeEntry", FormMethod.Post, new {
#class= "form-container" }))
{
<div id="times">
#{Html.RenderPartial("TimeTable", Model);}
</div>
<input name="Submit" class="form-control btn btn-primary" type="submit"
id="submit" value="Submit" />
}
View Model:
public class TimeFilter
{
public List<TimeItemWeekly> TimeItemWeekly { get; set; }
}
public class TimeItemWeekly
{
public string SelectedJob { get; set; }
public List<SelectListItem> Job { get; set; }
public string SelectedServiceItem { get; set; }
public List<SelectListItem> ServiceItem { get; set; }
}
Partial View:
#model NWwebappCS.Models.TimeFilter
#if (Model.TimeItemWeekly != null)
{
for (int i = 0; i < Model.TimeItemWeekly.Count(); i++)
{
<div class="row-container #(Model.TimeItemWeekly[i].HasError ?
<div class="row">
<input type="hidden" name="TimeItemWeekly.Index" value="#i" />
<div class="customer-details">
#Html.DropDownListFor(x => x.TimeItemWeekly[i].SelectedJob,
Model.TimeItemWeekly[i].Job, new { #class = "jobs select
form-control" })
</div>
<div class="service-details">
#if (Model.TimeItemWeekly[i].ServiceItem != null)
{
#Html.DropDownListFor(x =>
x.TimeItemWeekly[i].SelectedServiceItem,
Model.TimeItemWeekly[i].ServiceItem, new { #class =
"service-items select" })
}
else
{
<select name="TimeItemWeekly[#i].SelectedServiceItem"
class="service-items select">
<option></option>
</select>
}
</div>
</div>
</div>
}
}
Controller:
[System.Web.Http.HttpPost]
public ActionResult Submit(Models.TimeFilter Model, string submit)
{
switch (submit)
{
case "Submit":
string errorMessage = ValidateTime(Model);
FillLists(Model);
if (errorMessage == "")
{
DataTable timeRows = GetWeekData(Model);
DeleteTime(Model, timeRows);
SaveTime(Model, timeRows);
if (Model.TimeItemWeekly != null)
{
Model.TimeItemWeekly =
Model.TimeItemWeekly.OrderBy(x => x.Job.Where(y
=>
y.Selected).First().Text).ThenBy(x => x.ServiceItem.Where(y
=> y.Selected).First().Text).ToList();
}
GetUserInfo(Model);
HasPrivilege(Model);
TempData["SuccessMessage"] = "Changes Saved!";
return View("~/Views/TimeEntry/Index.cshtml",
Model);
}
else
{
TempData["ErrorMessage"] = errorMessage;
return View("~/Views/TimeEntry/Index.cshtml",
Model);
}
default:
return View("~/Views/TimeEntry/Index.cshtml", Model);
}
}
}
This happens because the indices get out of order and the mvc model binder can't bind the list properly because the name values which contain the index position are no longer sequential.
One way to solve this is to create an ajax call that returns your partial view view with an updated data set that doesn't contain the row you removed, and replacing the old partial HTML with a new one that has correct indices.
It would look something like this:
JS event for the remove button:
$(document).on("click", ".red-box", function () {
var id = 1; // this would be the id of the thing you are removing, if needed
$.get('/path/to/remove', { id: id }, function(data){
$("#times").html(data);
}
});
Action
public ActionResult Remove(int id) {
// Get data based on id and make any dB updates you need to make
var model = GetData(); // Get new NWwebappCS.Models.TimeFilter without removed row
return PartialView("TimeTable", model);
}
Something along those lines....

EditorForMany not working for objects deeper than 1 level

I have an example asp.net mvc5 program in which I'm trying to build a payment model with many levels of partials added to make a complete object. In this example, I am using generic data. I have a top level 'testing', to which you can add multiple 'A1' objects, and to that you can add multiple 'B2' objects.
The form uses ajax and jqueryto allow the person to add data on the fly, which is then submitted all at once when the submit button is pressed.
I found an html helper made by Matt Lunn that does an editorForMany. It works very well, adds all my info to the web page, but it will never post back a model that is deeper than 2 levels(top, with a1's attached).
I can get the entire model to build on my page. It looks appropriate, but when I post back, nothing under A1 shows up. I can add as many 'A1's as I want. If I change the code and put 'B2's directly under testing, that will work, but nothing will add under the 'A1's as I have it.
Here is my code. I apologize for the formatting and the length of this post.
MVC helper
public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> propertyExpression, Expression<Func<TValue, string>> indexResolverExpression = null, bool includeIndexField = true) where TModel : class
{
var items = propertyExpression.Compile()(html.ViewData.Model);
var htmlBuilder = new StringBuilder();
var htmlFieldName = ExpressionHelper.GetExpressionText(propertyExpression);
var htmlFieldNameWithPrefix = html.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
Func<TValue, string> indexResolver = null;
if (indexResolverExpression == null)
{
indexResolver = x => null;
}
else
{
indexResolver = indexResolverExpression.Compile();
}
foreach (var item in items)
{
var dummy = new { Item = item };
var guid = indexResolver(item);
var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));
var singleItemExp = Expression.Lambda<Func<TModel, TValue>>(memberExp, propertyExpression.Parameters);
if (String.IsNullOrEmpty(guid))
{
guid = Guid.NewGuid().ToString();
}
else
{
guid = html.AttributeEncode(guid);
}
if (includeIndexField)
{
htmlBuilder.Append(_EditorForManyIndexField<TValue>(htmlFieldNameWithPrefix, guid, indexResolverExpression));
}
htmlBuilder.Append(html.EditorFor(singleItemExp, null, String.Format("{0}[{1}]", htmlFieldName, guid)));
}
MvcHtmlString m1 = new MvcHtmlString(htmlBuilder.ToString());
return m1;
}
testing Controller
public class testingController : Controller
{
// GET: testing
public ActionResult startTest()
{
var model = new testing();
return View(model);
}
[HttpPost]
public ActionResult startTest([Bind] testing model)
{
if (ModelState.IsValid)
{
var r = 1;
}
return View(model);
}
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult addA1()//current running test
{
var model = new testing();
model.aas.Add(new A1());
return View(model);
}
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult addB2()//current running test
{
var model = new A1();
model.bbs.Add(new B2());
return View(model);
}
}
model class
public class testing
{
public string name { get; set; }
public List<A1> aas { get; set; }
public testing()
{
aas = new List<A1>();
}
}
public class A1
{
public string aName { get; set; }
public List<B2> bbs { get; set; }
public A1()
{
bbs = new List<B2>();
}
}
public class B2
{
public string bName { get; set; }
public B2() { }
}
startTest.cshtml
#model proofOfConceptPaymentBuilder.Models.testing
#{
ViewBag.Title = "startTest";
}
#section Scripts
{
<script>
jQuery(document).ready(function ($) {
$('#add-bbs').on('click', function () {
jQuery.get('/testing/addB2').done(function (html) {
$('#bbsList').append(html);
});
});
$('#add-aas').on('click', function () {
jQuery.get('/testing/addA1').done(function (html) {
$('#aasList').append(html);
});
});
});
function alertSomething() {
alert('something');
jQuery.get('/testing/addB2').done(function (html) {
$('#bbsList').append(html);
});
};
</script>
}
#using (Html.BeginForm())
{
<h2>Create</h2>
#Html.EditorFor(x => x)
<input type="submit" />
}
EditorTemplates
(testing editor template)
#model proofOfConceptPaymentBuilder.Models.testing
<div class="form-group">
#Html.LabelFor(x => x.name)
#Html.EditorFor(x => x.name)
</div>
<div class="form-group">
<div id="aasList">
#Html.EditorFor(x => x.aas)
</div>
<input type="button" id="add-aas" value="add aas" />
<input type="button" id="delete-testing" value="delete test" />
</div>
(a1 editor template)
#model proofOfConceptPaymentBuilder.Models.A1
<div class="form-group">
#Html.LabelFor(x => x.aName)
#Html.EditorFor(x => x.aName)
</div>
<div class="form-group">
<div id="bbsList">
#Html.EditorForMany(x => x.bbs)
</div>
<input type="button" id="add-bbs" value="add bss" onclick="alertSomething()"/>
<input type="button" id="delete-aas" value="delete ass" />
</div>
(b2 editor template)
#model proofOfConceptPaymentBuilder.Models.B2
<div class="form-group">
#Html.LabelFor(x => x.bName)
#Html.EditorFor(x => x.bName)
</div>
<div>
<input type="button" id="delete-bss" value="delete bbs" />
</div>
addA1.cshtml
#model proofOfConceptPaymentBuilder.Models.testing
#{
Layout = null;
}
#Html.EditorForMany(x => x.aas)
addB2.cshtml
#model proofOfConceptPaymentBuilder.Models.A1
#{
Layout = null;
}
#Html.EditorForMany(x => x.bbs)
Ok, so I'm sorry for the formatting. I'm kinda new to the formatting here. I had to manually indent my code for it to show up in code blocks.
I also stumbled upon this problem and, given the number of questions I have found here in StackOverflow dealing with similar problems, I have created a .NET Core library to solve exactly this. It is called DynamicVML (Dynamic View-Model Lists) and you can get it with NuGet.
It is basically a list templating engine for ASP.NET Core, that you can use to display lists of view models of any depth.
You can use it like this:
#Html.ListEditorFor(x => x.Addresses,
Url.Action("AddAddress"), // the action in your controller that creates views
"Add new address", // some text for the "add new item button" in your form
listContainerTemplate: "viewThatWrapsTheList",
listTemplate: "viewForTheList",
itemContainerTemplate: "viewThatWrapsYourViewModel",
ItemTemplate: "viewForYourViewModel")
Now, you do not actually have to specifiy all of this. If you want you can do just
#Html.ListEditorFor(x => x.Addresses, Url.Action("AddAddress"), "Add new address")
And it will guess all the rest.
It will support nesting with any depth, as it was the core of the question.

ASP.NET index page with search posting into partial view

I have an Index page with a search box (straight from this tutorial: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application) to which I've added a Create form in the footer, which I display as a partial view.
Although the Create form works well and adds new records, the search form seems to post back the Create Form instead, causing a validation error, and redisplaying the whole layout.cshtml page within the location of my partial view.
Edit - the search form posts itself and returns the correct result and then seems to post my partial view as well. My debugger shows the controllers ActionResult Create HTTP post function being called
How do I get the Search form to stop also posting into my partial view?
My index.cshml:
#using (Html.BeginForm("Index"))
{
<p>
#Html.TextBox("SearchString", ViewBag.CurrentFilter as string, new { #class = "search-query", placeholder = "Search by name" })
<input type="submit" value="Search" class="btn" />
</p>
}
#Html.Action("Create");
My Create.cshtml:
#using (Html.BeginForm("Create"))
{
#Html.TextBoxFor(model => model.Title, new { #style = "width:250px" })
#Html.TextBoxFor(model => model.AnnouncementText, new { #style = "width:250px" })
<input type="submit" value="Create" class="btn btn-small" />
#Html.ValidationMessageFor(model => model.Title)
#Html.ValidationMessageFor(model => model.AnnouncementDate)
}
My controller:
public ViewResult Index(string searchString, int? page)
{
var Announcements = from a in db.Announcements
select a;
if (!String.IsNullOrEmpty(searchString))
{
ViewBag.Search = true;
Announcements = Announcements.Where(s => (s.Title.ToUpper().Contains(searchString.ToUpper()) || s.AnnouncementText.ToUpper().Contains(searchString.ToUpper())));
}
Announcements = Announcements.OrderBy(s => s.Title);
int pageSize = 10;
int pageNumber = (page ?? 1);
return View(Announcements.ToPagedList(pageNumber, pageSize));
}
public ActionResult Create()
{
Announcement announcement = new Announcement();
return PartialView(announcement);
}
//
// POST: /Announcement/Create
[HttpPost]
public ActionResult Create(Announcement announcement)
{
if (ModelState.IsValid)
{
db.Announcements.Add(announcement);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(announcement);
}
One possible solution is to use ajax to post a partial back to the view without a full page reload. If you use Ajax.BeginForm{} then your controller can return your partial as html as a model property and then you just load the content on the Ajax.BeginForm's successFunction. I use the method below in a base controller class as I often return partial content in views.
Model...
public class BaseEditModel
{
public string PostAction{get;set;}
public string PostController{get;set;}
public string PartialViewContent{get;set;}
}
Controller...
public ActionResult(int someID)
{
BaseEditModel model=new BaseEditModel();
model.PostController="SaveController";
model.PostAction="SaveEntity";
model.PartialViewContent=this.RenderPartialViewToString("Partials/Entity/EntityEdit", model);
}
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
View...
#using(Ajax.BeginForm(
Model.PostAction,
Model.PostController,
null,
new AjaxOptions { OnSuccess = "editPostSuccess", OnFailure = "editPostFailure" },
new { id = "editBase_frmEdit", name = "editBase_frmEdit" }))
{
}
<script type="text/javascript">
function editPostSuccess(ajaxContext) {
if (ajaxContext.PartialViewContent != null)
$('#partialDiv').html(ajaxContext.PartialViewContent);
}
I still don't entirely understand what caused the problem, but I fixed it by renaming the Create Post method to CreatePost. When the GET and POST methods both had the same name, for some reason the POST was being called.
My create.cshtml
#using (Html.BeginForm("CreatePost"))
Thanks all for your help

ASP.NET MVC redirect actions error with 'create' partial view on 'index' page

I'm having a problem with partial views. I have an index view of Announcements and I'm trying to add a partial view to create a new Announcement within the same page.
I can display the partial view, and submit the form to create a new record. The record gets submitted into the database, but when re-rendering the page, I get the error: Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper', {"Child actions are not allowed to perform redirect actions."} on my Html.Action statement in my index page.
I've been struggling to make this work, and have firstly changed the Html.Partial to a Html.Action statement as the controller methods weren't firing, then secondly, after I read that this error is because while rendering the page, .NET doesn't know what my redirect action is doing so automatically stops it, tried changing the Html.Action to Html.RedirectAction inside a code block, but still get the same error detailed above.
My model is quite simple:
public class Announcement
{
public Announcement()
{
AnnouncementDate = System.DateTime.Now;
}
[Key]
public int AnnouncementID { get; set; }
public string Title { get; set; }
public string Type { get; set; }
}
My Controller methods:
public ViewResult Index(string searchString, int? page)
{
var Announcements = from a in db.Announcements
select a;
if (!String.IsNullOrEmpty(searchString))
{
Announcements = Announcements.Where(s => (s.Title.ToUpper().Contains(searchString.ToUpper()) || s.AnnouncementText.ToUpper().Contains(searchString.ToUpper())));
}
Announcements = Announcements.OrderBy(s => s.Title);
int pageSize = 10;
int pageNumber = (page ?? 1);
return View(Announcements.ToPagedList(pageNumber, pageSize));
}
//
// GET: /Announcement/Create
public ActionResult Create()
{
Announcement announcement = new Announcement();
return PartialView(announcement);
}
//
// POST: /Announcement/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Announcement announcement)
{
if (ModelState.IsValid)
{
db.Announcements.Add(announcement);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(announcement);
}
Index.cshtml
#model PagedList.IPagedList<Project.Models.Announcement>
#using PagedList.Mvc;
#using PagedList;
#using (Html.BeginForm())
{
#Html.TextBox("SearchString", ViewBag.CurrentFilter as string, new { #class = "search-query", placeholder = "Search by name" })
<input type="submit" value="Search" class="btn" />
}
#item.Title
#item.Type
#Html.Action("Create"); // This is the line causing errors after I submit the Create form. Have tried changing to Html.RedirectAction
Create.cshtml:
#model Project.Models.Announcement
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(model => model.Title, new { #style = "width:250px" })
#Html.TextBoxFor(model => model.Type, new { #style = "width:250px" })
<input type="submit" value="Create" class="btn btn-small" />
}
After doing some testing locally...
You can keep
#Html.Action("Create")
However, you have to change one small thing. Define what action the POST points to in your form :)
#model Project.Models.Announcement
#using (Html.BeginForm("Create"))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(model => model.Title, new { #style = "width:250px" })
#Html.TextBoxFor(model => model.Type, new { #style = "width:250px" })
<input type="submit" value="Create" class="btn btn-small" />
}

MVC3- Passing an entire model with dynamic data from view to controller

I have two models: OuterModel and InnerModel. There is a one to many relationship between OuterModel and InnerModel, respectively. To clarify my question, my model is of type IEnumerable<OuterModel>. I'm passing a random number of OuterModels to the view and the user creates any number of InnerModels for each OuterModel. Then on submission, I want the controller to receive the list of OuterModels so that the InnerModels can be added to the database to their intended OuterModels.
I believe I have the naming convention correct to make use of MVC's built in model binding. Here's what that looks like:
OuterModel[i].InnerModel[j].Property
My problem is, I don't really know how to get a list of OuterModels passed to the controller. Here's what I've tried in my View:
#model IEnumerable<OuterModel>
#using (Html.BeginForm("Create", "Controller", new { OuterModels = Model }, FormMethod.Post))
{
//Code to create the InnerModels here
}
And here's what I have in my Controller:
[HttpPost]
public ActionResult Create(IEnumerable<OuterModel> OuterModels, FormCollection fc)
{
String[] keys = fc.AllKeys;
if(ModelState.IsValid){
//Add to db
}
}
Keys shows that all of my properties are following the naming convention that I specified earlier, but ModelState.IsValid is returning false. It shows that OuterModels' count is 0.
Even though I'm telling the form to submit OuterModels = Model before any InnerModels are created, you would think there would still be data in OuterModels considering it's passed to the view. I am really tired today, so I'm guessing I'm looking over one (or many) small detail(s). Any suggestions?
--EDIT--
Passing a list of OuterModels to the controller may not be the best approach. If anybody has a better suggestion, please share.
As long as indexes are used properly, then this should not be an issue. Here is how I would envision the form names.
Model[0].foo
Model[0].Inner[0].bar
Model[0].Inner[1].bar
Where outer model has a property called foo and
Outer model has a property called inner which is a collection of inner objects. Inner object has a property called bar. If your form is rendered with the correct indexes then the model binding should work. Things can get tricky if form fields are generated client side. I recommended going back to server in order to manipulate the model. There are some extra round trips, but you can make them via Ajax request.
Here are some more details in a more fleshed out example.
public class InnerModel{
public string Name{get; set;}
}
public class OuterModel{
public List<InnerModel> InnerModels{get; set;}
public string Name{get; set;}
}
Here is what I would envision my view would look like:
#model IEnumerable<OuterModel>
<ul>
#{int i = 0;}
#foreach(var item in Model){
<li>
Outer Name : #Html.TextBoxFor(m=>Model[i].Name)
<br />
#{int j = 0;}
<ul>
#foreach(var innerItem in Model[i].InnerModels){
<li>Inner Name : #Html.TextBoxFor(m=> Model[i].InnerModels[j].Name)</li>
j++;
}
</ul>
i++;
</li>
}
</ul>
If this is wrapped in a form--- and the controller action looks like this:
public ActionResult Action(List<OuterModel> model){
}
then I would think model would be populated correctly.
I noticed your form.. it doesn't look right to me... I wouldn't think that the passing the OuterModels like that is going to work-- although frankly I might be wrong.
#using (Html.BeginForm("Create", "Controller", new { OuterModels = Model }, FormMethod.Post))
{
//Code to create the InnerModels here
}
Here is an example I did for the class I teach.. that definitely works..
public class Author
{
public string Name { get; set; }
}
public class Book
{
public string Name { get; set; }
public List<Author> Authors { get; set; }
}
Controller:
public class BookController : Controller
{
public static List<Book> _model = null;
public List<Book> Model
{
get
{
if (_model == null)
{
_model = new List<Book>
{
new Book{
Name = "Go Dog Go",
Authors = new List<Author>{
new Author{Name = "Dr. Seuss"}
}},
new Book{
Name = "All the Presidents Men",
Authors = new List<Author>{
new Author{Name = "Woodward"},
new Author{Name = "Bernstein"}
}},
new Book{
Name = "Pro ASP.NET MVC Framework",
Authors = new List<Author>{
new Author{Name = "Sanderson"},
new Author{Name = "Stewart"},
new Author {Name = "Freeman"}
}}
};
}
return _model;
}
}
public ActionResult Index()
{
return View(Model);
}
public ActionResult Edit()
{
return View(Model);
}
[HttpPost]
public ActionResult Edit(List<Book> books)
{
_model = books;
return RedirectToAction("Index");
//return View(books);
}
}
and View:
#model List<AmazonWeb.Models.Book>
#{
ViewBag.Title = "Index";
}
<div class="content">
#Html.ActionLink("Index", "Index")
#using (Html.BeginForm())
{
<input type="submit" value="save" />
<ul class="book-list">
#for (var i = 0; i < Model.Count; i++ )
{
<li>
<label>Book Name</label> : #Html.TextBoxFor(m => Model[i].Name)
<ul>
#for (var j = 0; j < Model[i].Authors.Count; j++ )
{
<li><label>Author Name</label> : #Html.TextBoxFor(m => Model[i].Authors[j].Name)</li>
}
</ul>
</li>
}
</ul>
<input type="submit" value="save" />
}
</div>

Resources