MVC Razor POST - list object is always null - c#

I've tried several times to get my complex object which contains a list to POST correctly. All the properties except the list object contain values when it posts. I've based my approach on this SO Q&A and this SO Q&A. However, in both case the solution is to use a for loop instead of a foreach loop. I'm using the recommended for loop but still have\ing an issue. I'm not sure what else could be causing the list to be null when posting. Thanks in advance for your help!
View:
#model PropertiesAdminSite.Models.UtilityData
#{
ViewBag.Title = "CreateNewCycle";
}
<h2>New Residence Utilities</h2>
#using (Html.BeginForm("Upload", "ImportWater", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="control-group">
#Html.TextBoxFor(m => m.UploadFile, new { type = "file"})
</div>
<div class="control-group">
<input type="submit" class="btn btn-info" value="Upload" />
</div>
<div class="col-lg-12 visible-lg">
<br>
<span style="color:green">#ViewBag.Message</span>
#Html.HiddenFor(model => model.bID)
#Html.HiddenFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bPrevDate)
#for (int i = 0; i < Model.utilData.Count(); i++)
{
#Html.HiddenFor(model => model.utilData[i].ResNumber)
#Html.HiddenFor(model => model.utilData[i].GrnLower)
#Html.HiddenFor(model => model.utilData[i].GrnUpper)
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
#Html.HiddenFor(model => model.utilData[i].rID)
#Html.HiddenFor(model => model.utilData[i].WaterReading)
#Html.HiddenFor(model => model.utilData[i].wDifference)
#Html.HiddenFor(model => model.utilData[i].YelLower)
#Html.HiddenFor(model => model.utilData[i].YelUpper)
}
</div>
}
#using (Html.BeginForm("IndexMulti", "Utilities", FormMethod.Post))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
#Html.LabelFor(model => model.bEntryDate, htmlAttributes: new { #class = "control-label col-md-1" })
#Html.DisplayFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bID)
#Html.HiddenFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bPrevDate)
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class="dataTable_wrapper">
<!--div id="dataTables-example_wrapper" class="dataTables_wrapper form-inline dt-bootstrap no-footer">-->
<div class="row">
<div class="col-sm-12">
<table class="table table-striped table-bordered table-hover dataTable no-footer" id="dataTables-Bills" role="grid" aria-describedby="dataTables-example_info">
<!-- /table headers-->
<thead>
<tr role="row">
<th>#Html.DisplayNameFor(model => model.utilData.First().ResNumber)</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().WaterReading)</th>
<th>
#Html.DisplayNameFor(model => model.utilData.First().prevWaterReading)
#Html.DisplayFor(model => model.bPrevDate)
</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().wDifference)</th>
<th>Actions</th>
</tr>
</thead>
<!-- /table body-->
<tbody>
#for (int i = 0; i < Model.utilData.Count(); i++)
{
<tr role="row">
<td>
#Html.DisplayFor(modelItem => modelItem.utilData[i].ResNumber)
#Html.HiddenFor(model => model.utilData[i].GrnLower)
#Html.HiddenFor(model => model.utilData[i].GrnUpper)
#Html.HiddenFor(model => model.utilData[i].YelLower)
#Html.HiddenFor(model => model.utilData[i].YelUpper)
</td>
<td>
#Html.EditorFor(model => model.utilData[i].WaterReading)
</td>
<td>
<span id="#string.Format("prevWater_{0}",Model.utilData[i].rID)">
#Html.DisplayFor(model => model.utilData[i].prevWaterReading)
</span>
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
</td>
<td>
<span id="#string.Format("hdifference_{0}",Model.utilData[i].rID)">
#Html.DisplayFor(model => model.utilData[i].wDifference)
</span>
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
</td>
<td>
#Html.ActionLink("View History", "ExportDataIndex", "ExportData", new { rID = Model.utilData[i].rID, bId = Model.bID }, null) |
<a href="#Url.Action("ExportToExcel", "ExportData", new { rID = Model.utilData[i].rID, bId = Model.bID })" class="btn btn-success">
<i class="fa fa-file-excel-o" aria-hidden="true" title="Export to Excel"></i>
</a> |
<a href="#Url.Action("ChartData", "Utilities", new { rID = Model.utilData[i].rID, bId = Model.bID })" class="btn btn-info">
<i class="fa fa-bar-chart" aria-hidden="true" title="Water Usage History"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-0 col-md-8">
<input type="submit" value="Submit Water Readings and Create Invoices" class="btn btn-primary btn-lg btn-block" />
</div>
</div>
}
Model:
public partial class UtilityData
{
public DateTime bEntryDate { get; set; }
public string bPrevDate { get; set; }
public int bID { get; set; }
//public int residenceCount { get; set; }
public List<UtilEntry> utilData { get; set; }
public HttpPostedFileBase UploadFile { get; set; }
}
public partial class UtilEntry
{
public int rID { get; set; }
public long? WaterReading { get; set; }
public int ResNumber { get; set; }
public long? prevWaterReading { get; set; }
public decimal wDifference { get; set; }
public int GrnUpper { get; set; }
public int GrnLower { get; set; }
public int YelUpper { get; set; }
public int YelLower { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexMulti([Bind(Include = "bEntryDate, bPrevDate,bID,uData")] UtilityData uData)
{
if (ModelState.IsValid)
{
Omitted for clarity
}
return RedirectToAction("Create", "TenantAccount", new { id = uData.bID});
}

Per #Stephen Muecke I had a typo in the IndexMulti Binding:
public ActionResult IndexMulti([Bind(Include = "bEntryDate, bPrevDate,bID,utilData")] UtilityData uData)
utilData was udata originally.

Related

How to send IEnumerable back from View to Controller

I'm pretty new to MVC and ASP.Net, my controller has 2 create methods for GET and POST. Through the Get method I'm generating a list and sending it into the view. In the view I'm making changes to a specific value in each var in the list and trying to send it back to the controller and the POST method.
When reaching the Post method, the value of the list is null.
The list I'm sending is a list of products and I'm using a view model to create a class with common values to pass through to the view.
To pass through the IEnumerable collection that was edited by the view I tried using BeginCollectionForm, setting the items through ViewBag, make changes to the model (#model IEnumerable<MarsBurgerV1.ViewModel.ItemVM>), but still each time the checkout button is being pressed the list in the Post method is NULL.
After a lot of tries and changes, currently my code looks as the following:
OrderController.cs (relevant parts)
public class OrderController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Create()
{
var meals = (from m in db.meals
select new ItemVM
{
Id = m.Id,
Name = m.Name,
Price = m.Price,
ItemType = "Meal",
ImageURL = m.ImageUrl,
Quantity = 0
}).ToList();
var drinks = (from d in db.drinks
select new ItemVM
{
Id = d.Id,
Name = d.Name,
Price = d.Price,
ItemType = "Drink",
Quantity = 0
}).ToList();
//More Like That....
List<ItemVM> items = new List<ItemVM>();
items.AddRange(meals);
items.AddRange(drinks);//More Like...
return View(items.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IEnumerable<ItemVM> items = null)
{
//Here items equals to null.
if (ModelState.IsValid)
{
//.....
}
return View();
}
Views/Order/Create.cshtml:
#model IEnumerable<***.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
var lst = Model.ToList();
ViewBag.List = Model.ToList();
}
<style>
tr td {
vertical-align: middle;
}
</style>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayNameFor(m => m.Name)
</th>
<th>
#Html.DisplayNameFor(m => m.Price)
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#if (Model.ElementAt(i).ImageURL != null)
{
<img src="#Url.Content(Model.ElementAt(i).ImageURL)" alt="IMAGES" height="100" width="100" />
}
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Name)
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Price)
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(l => lst[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => lst[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
ViewModel/ItemVM.cs:
namespace ***.ViewModel
{
public class ItemVM
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
[Required]
public string ItemType { get; set; }
public string ImageURL { get; set; }
[Required]
public int Quantity { get; set; }
}
}
In the end I want to use the "Create" (HttpPost method) to create a new order based on the values in the list received from the view.
What is the proper way of doing that and receiving the IEnumerable into the POST method?
Ok, I was finally able to make it work.
I've changed the #model to List Type,
Add the (actionName, Controller, FormMethod) to the HTML Helper Html.BeginForm,
Used Model[i] inside the loop to access variables and
marked all unchanged variables with
#Html.HiddenFor.
Create.cshtml:
#model List<MarsBurgerV1.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm("Create","Order", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayName("Name")
</th>
<th>
#Html.DisplayName("Price")
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
#Html.HiddenFor(m => Model[i].Id)
#Html.HiddenFor(m => Model[i].Type)
<td>
#Html.HiddenFor(m => Model[i].ImageURL)
#if (Model[i].ImageURL != null)
{
<img src="#Url.Content(Model[i].ImageURL)" alt="IMAGES" height="100" width="100" />
}
#Html.ValidationMessageFor(model => Model[i].ImageURL, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Name)
#Html.DisplayFor(m => Model[i].Name)
#Html.ValidationMessageFor(model => Model[i].Name, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Price)
#Html.DisplayFor(m => Model[i].Price, new { #class = "form-control" })
#Html.ValidationMessageFor(model => Model[i].Price, "", new { #class = "label-control" })
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(model => Model[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => Model[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
Thanks Everyone for the help.

Cannot get the Partialview Model in Parent Controller

I am creating a DataEntry Screen which have a three dropdownlist at the top
which is cascaded using ajax . And A partial view is also rendered using Ajax which will show all the items against that selection user made on the dropdowns. The partialview consist of a table with multiple rolls.
I am not able to get the collection of data selected by the user in the partial view (table )in my controller.
My Controller
[HttpGet]
public ActionResult ApproveLaysheet()
{
LaySheetShortageViewModel mdl = new LaySheetShortageViewModel();
ViewBag.AtcID = new SelectList(db.AtcMasters.Where(o => o.IsClosed == "N"), "AtcId", "AtcNum");
return View(mdl);
}
[HttpGet]
public PartialViewResult GetRollView(decimal[] SelectedOurStyle)
{
LaySheetShortageViewModel model = new LaySheetShortageViewModel();
LaysheetRollRepository lyipores = new LaysheetRollRepository();
model.rolldetailcollection= lyipores.getlaysheetRollData(SelectedOurStyle);
return PartialView("LaySheetRollView",model);
}
[HttpPost]
public ActionResult ApproveLaysheet(LaySheetShortageViewModel Model)
{ // not gretting the value of rolldetailcollection here
return View();
}
My View
#model ArtWebApp.Areas.ArtMVC.Models.ViewModel.LaySheetShortageViewModel
<script type="text/javascript">
$(document).ready(function () {
$("#Show").click(function (e, params) {
debugger;
var SelectedOurStyle = new Array();
SelectedOurStyle = $("#LaySheetID").chosen().val();
if (SelectedOurStyle != null)
{
$.ajax({
url: "#Url.Action("GetRollView", "ApproveLaysheet")",
traditional: true,
data: { 'SelectedOurStyle': SelectedOurStyle },
type: "GET",
success: function (fooBarHTML) {
$("#output").html(fooBarHTML);
},
error: function (xhr, status, errorThrown) {
//...
}
});
}
});
});
</script>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SampCutReqMaster</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("Atcc#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("AtcID", null, htmlAttributes: new { #class = "chosen-select form-control" })
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("OurStyle#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("OurStyleID", new MultiSelectList(string.Empty, "Value", "Text") , null, htmlAttributes: new { #class = "chosen-select form-control", #multiple = "multiple" } )
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-2">
#Html.Label("LaySheet#", new { #class = "control-label col-md-2" });
</div>
<div class="col-md-10">
#Html.DropDownList("LaySheetID", new MultiSelectList(string.Empty, "Value", "Text"), null, htmlAttributes: new { #class = "chosen-select form-control", #multiple = "multiple" })
</div>
</div>
</div>
<div id='output' class="">
<!-- Partialview Comes here -->
</div>
</div>
}
My Partial View
#using HtmlHelpers.BeginCollectionItem
#model ArtWebApp.Areas.ArtMVC.Models.ViewModel.LaySheetShortageViewModel
<script src="~/JQuery/GridJQuery.js"></script>
<script>
</script>
<div class="container">
<table class="table table-bordered table-striped table-responsive">
<tr>
<th>
#Html.CheckBox("SelectAll")
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.LaySheetDet_PK)
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.LayCutNum)
</th>
<th>
#Html.DisplayNameFor(model => model.approvelaysheetModel.RollNum)
</th>
</tr>
#if (Model != null)
{
for (int i = 0; i < Model.rolldetailcollection.Count; i++)
{
using (Html.BeginCollectionItem("rolldata"))
{
<tr>
<td>
#Html.EditorFor(modelItem => Model.rolldetailcollection[i].IsSelected, new { #onclick = "Check_ClickNew(this)" })
</td>
<td>
#Html.EditorFor(modelItem => Model.rolldetailcollection[i].LaySheetDet_PK)
#Html.HiddenFor(model => Model.rolldetailcollection[i].LaySheetDet_PK, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.DisplayFor(modelItem => Model.rolldetailcollection[i].LayCutNum)
</td>
<td>
#Html.DisplayFor(modelItem => Model.rolldetailcollection[i].RollNum)
</td>
</tr>
}
}
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="Submit" type="submit" value="Submit Fabric request" class="btn btn-default show" />
</div>
</div>
</div>
My viewModals
public class ApprovelaysheetModel
{
public Decimal ApprovelaysheetModelID { get; set; }
[Display(Name ="ID")]
public Decimal LaySheetDet_PK { get; set; }
public Boolean IsSelected { get; set; }
public Decimal LaySheet_PK { get; set; }
}
public class LaySheetShortageViewModel
{
[Key]
[Display(Name = "ID")]
public Decimal ShortageID { get; set; }
public int Location_pk { get; set; }
public int Atcid { get; set; }
public int OurstyleID { get; set; } }
public List<ApprovelaysheetModel> rolldetailcollection { get; set; }
}
Can anyone suggest whats my mistake or any better method for doing this dataentry as Iam new to MVC
Your use of BeginCollectionItem() inside the for loop is prefixing your name attributes so that they no longer relate to your models properties. Instead of generating
<input type="checkbox" name="rolldetailcollection[0].IsSelected" ... />
your now generating
<input type="checkbox" name="rolldata[####].rolldetailcollection[0].IsSelected" ... />
where #### is a Guid.
Remove the BeginCollectionItem() code so that your loop is
for (int i = 0; i < Model.rolldetailcollection.Count; i++)
{
<tr>
<td>
#Html.EditorFor(m => m.rolldetailcollection[i].IsSelected, new { #onclick = "Check_ClickNew(this)" })
</td>
<td>
#Html.EditorFor(m => m.rolldetailcollection[i].LaySheetDet_PK)
// Note there is no point adding html attributes for a hidden input
#Html.HiddenFor(m => m.rolldetailcollection[i].LaySheetDet_PK)
</td>
....
</tr>
}
Note that the BeginCollectionItem() method is used when you want to dynamically add and remove items from a collection in the view in conjunction with javascript/ajax (refer this answer for an example). If you do want to do that, then you need to create a partial view (say) _ApprovelaysheetModel.cshtml for the model
#model ApprovelaysheetModel
// Note the name needs to match the collection property name
#using (Html.BeginCollectionItem("rolldetailcollection"))
{
....
#Html.EditorFor(m => m.LaySheetDet_PK)
....
}
and then in the main view you use a foreach loop
#foreach(var item in Model.rolldetailcollection)
{
#Html.Partial("_ApprovelaysheetModel", item)
}

Cannot pass viewmodel with list to controller-action (MVC 5)

I am having problem passing a viewmodel that contains a list of "files" from my view to my controller(action). The viewmodel seems to be passed to the actionresult with all the propetries, except the list (List Files). The list just stays null, even if the model contains files when i submit the form.
What do i do wrong? I will paste my c# code from my controller, model, viewmodel and my RazorHTML.
File-model:
public class File
{
[Key]
public int ID { get; set; }
[Display(Name = "Filnamn")]
public string Filename { get; set; }
//[Display(Name = "Beskrivning")]
//public string Description { get; set; }
[Display(Name = "Filtyp")]
public string ContentType { get; set; }
public byte[] Data { get; set; }
public int Size { get; set; }
//public string CreatorId { get; set; }
public string CreatorID { get; set; }
[Required]
[Display(Name = "Skapare")]
[ForeignKey("CreatorID")]
public virtual ApplicationUser Creator { get; set; }
//public int DocumentId { get; set; }
public int DocumentID { get; set; }
[Required]
[Display(Name = "Dokument")]
[ForeignKey("DocumentID")]
public virtual Document Document { get; set; }
}
Viewmodel:
public class CreateDocumentViewModel
{
public CreateDocumentViewModel()
{
CheckboxUsers = new List<CheckBoxListUser>();
CheckboxGroups = new List<CheckBoxListGroup>();
CheckboxTags = new List<CheckBoxListTags>();
Files = new List<File>();
}
[Display(Name = "Beskrivning")]
public string Description { get; set; }
[Display(Name = "Titel")]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
[Display(Name = "Markdown")]
public string Markdown { get; set; }
HttpPostedFileBase FileToAdd { get; set; }
[Display(Name="Användare")]
public List<CheckBoxListUser> CheckboxUsers { get; set; }
[Display(Name = "Grupper")]
public List<CheckBoxListGroup> CheckboxGroups { get; set; }
[Display(Name = "Taggar")]
public List<CheckBoxListTags> CheckboxTags { get; set; }
[Display(Name = "Filer")]
public ICollection<File> Files { get; set; }
public string FilesJson { get; set; }
}
View:
#model MarkdownManagerNew.Viewmodels.CreateDocumentViewModel
#{
ViewBag.Title = "CreateDocument";
}
<h2>Nytt dokument</h2>
#Html.BeginForm("CreateFile", "User", new { model = Model, test = Model.Name, files = Model.Files }, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", placeholder = "Titel" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", placeholder = "Beskrivning" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group col-md-6 markdownEditorForm">
<div class="col-md-12">
#Html.EditorFor(model => model.Markdown, new { htmlAttributes = new { #class = "form-control markdownEditor" } })
#Html.ValidationMessageFor(model => model.Markdown, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-sm-6 markdownResult"></div>
</div>
</div>
<div class="row">
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.CheckboxGroups)
</dt>
#for (int i = 0; i < Model.CheckboxGroups.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxGroups[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxGroups[i].ID)
#Html.HiddenFor(x => x.CheckboxGroups[i].Display)
#Html.DisplayFor(x => x.CheckboxGroups[i].Display)
</dd>
}
<dt>
#Html.DisplayNameFor(model => model.CheckboxUsers)
</dt>
#for (int i = 0; i < Model.CheckboxUsers.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxUsers[i].ID)
#Html.HiddenFor(x => x.CheckboxUsers[i].Display)
#Html.DisplayFor(x => x.CheckboxUsers[i].Display)
</dd>
}
<dt>
#Html.DisplayNameFor(model => model.CheckboxTags)
</dt>
#for (int i = 0; i < Model.CheckboxTags.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxTags[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxTags[i].ID)
#Html.HiddenFor(x => x.CheckboxTags[i].Display)
#Html.DisplayFor(x => x.CheckboxTags[i].Display)
</dd>
}
</dl>
</div>
<div class="form-group">
#Html.Label("File", new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="File" name="upload" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Ladda upp fil" class="btn btn-default" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#if (Model.Files.Count > 0)
{
<h3>#Html.DisplayNameFor(model => model.Files)</h3>
<table class="table table-striped table-bordered">
<tr>
<th>Filename</th>
<th>Filetype</th>
<th>Size(bytes)</th>
</tr>
#foreach (var file in Model.Files)
{
<tr>
<td>#file.Filename</td>
<td>#file.ContentType</td>
<td>#file.Size</td>
</tr>
}
</table>
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-1 col-md-10">
<input type="submit" value="Skapa" class="btn btn-default" />
</div>
</div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
[HttpPost]
public ActionResult CreateFile(CreateDocumentViewModel viewModel)
{
var file = repo.CreateFile(upload, GetCurrentUser());
viewModel.Files.Add(file);
TempData["viewModel"] = viewModel;
//return RedirectToAction("CreateDocument", file);
return RedirectToAction("CreateDocument", new { files = viewModel.Files });
}
-EDIT-
I have also tried adding editorfields for every property in every file like this:
#for (int i = 0; i < Model.Files.Count; i++)
{
<div>
#Html.LabelFor(x => x.Files[i].DocumentID)
#Html.EditorFor(x => x.Files[i].DocumentID)
#Html.ValidationMessageFor(x => x.Files[i].DocumentID)
#Html.LabelFor(x => x.Files[i].CreatorID)
#Html.EditorFor(x => x.Files[i].CreatorID)
#Html.ValidationMessageFor(x => x.Files[i].CreatorID)
#Html.LabelFor(x => x.Files[i].ID)
#Html.EditorFor(x => x.Files[i].ID)
#Html.ValidationMessageFor(x => x.Files[i].ID)
#Html.LabelFor(x => x.Files[i].ContentType)
#Html.EditorFor(x => x.Files[i].ContentType)
#Html.ValidationMessageFor(x => x.Files[i].ContentType)
#Html.LabelFor(x => x.Files[i].CreatorID)
#Html.EditorFor(x => x.Files[i].CreatorID)
#Html.ValidationMessageFor(x => x.Files[i].CreatorID)
#Html.LabelFor(x => x.Files[i].Data)
#Html.EditorFor(x => x.Files[i].Data)
#Html.ValidationMessageFor(x => x.Files[i].Data)
#Html.LabelFor(x => x.Files[i].Filename)
#Html.EditorFor(x => x.Files[i].Filename)
#Html.ValidationMessageFor(x => x.Files[i].Filename)
#Html.LabelFor(x => x.Files[i].Size)
#Html.EditorFor(x => x.Files[i].Size)
#Html.ValidationMessageFor(x => x.Files[i].Size)
</div>
}
Your files are of type ICollection thus each file information needs to be present within the form to be able to be posted to the controller in the populated viewmodel. Currently you have no fields for this collection and such the ICollection is empty when posted to the controller. You can create an custom EditorFor to display the File data as well as post this data to the controller.
Please refer to this tutorial on how to do this:
http://blog.learningtree.com/en/editing-collections-in-asp-net-mvc/
In your code you are doing something similar for the checkboxes:
#for (int i = 0; i < Model.CheckboxUsers.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxUsers[i].ID)
#Html.HiddenFor(x => x.CheckboxUsers[i].Display)
#Html.DisplayFor(x => x.CheckboxUsers[i].Display)
</dd>
}
Note the hidden fields, this is what is passing the data to the controller for the list of checkboxes. I would reccomend creating a custom EditorFor for the files. Or more simply repopulate the collection of files again in your controller from the model.
But the Files collection is not in any inputs. They are basically just labels, therefore they will not post to server. Since they do not change by user, you can just load the files from DB again in your action.

List of objects is null when posting to MVC action when using for loop

I have posted a list of objects to an MVC action just fine in the past but I am not sure why I am getting null results on the occasion.
I have created a custom view-model:
public class CustomPmScheduleGenerator
{
public bool Save { get; set; }
public bool Search { get; set; }
public DateTime? BegDueDate { get; set; }
public DateTime? EndDateDate { get; set; }
public string GenSn { get; set; }
public string PmName { get; set; }
public string Type { get; set; }
public IList<stp_GetPmSchedulesGenerator_Result> Pms { get; set; }
public IList<stp_GetRepairSchedulesGenerator_Result> Repairs { get; set; }
}
The above object is what I am posting to the index action. Both Pms and Repairs are coming back as null.
Here is my actions (GET and POST):
[HttpGet]
public ActionResult Index()
{
var model = new CustomPmScheduleGenerator();
var pms = _db.stp_GetPmSchedulesGenerator(null, null, Convert.ToInt32(Session["CustomerNumber"]), null, null).ToList();
var repairs = _db.stp_GetRepairSchedulesGenerator(null, null, Convert.ToInt32(Session["CustomerNumber"]), null, null).ToList();
model.Pms = pms;
model.Repairs = repairs;
return View(model);
}
[HttpPost]
public ActionResult Index(CustomPmScheduleGenerator model)
{
if (model.Save)
{
//Update the PMs
foreach (var pm in model.Pms)
{
//TODO: Update the current PM
}
//Update the Repairs
foreach (var repair in model.Repairs)
{
//TODO: Update the Current Repair
}
}
else if (model.Search)
{
var pms = _db.stp_GetPmSchedulesGenerator(model.BegDueDate, model.EndDateDate, Convert.ToInt32(Session["CustomerNumber"]),model.PmName, model.GenSn).ToList();
var repairs = _db.stp_GetRepairSchedulesGenerator(null, null, Convert.ToInt32(Session["CustomerNumber"]), null, null).ToList();
model.Pms = pms;
model.Repairs = repairs;
return View(model);
}
return View();
}
I have put a break point on the POST action on the if statement line and check the values of the parameter "model" and that is where I am seeing the null values.
Here is what my view looks like:
#using (Html.BeginForm("Index", "PmScheduleGenerator", FormMethod.Post))
{
<div>
<h1>
<strong>PM Schedule</strong>
#Html.ActionLink("Add Repair", "AddRepair", null, new { #class = "btn btn-warning", #style = "float:right;margin-left:5px;" })
#Html.ActionLink("Save", "AddRepair", null, new { #class = "btn btn-success", #style = "float:right" })
</h1>
</div>
<hr />
<div class="row">
<div class="col-lg-2 form-group">
<div class="editor-label">
#Html.Label("Due Beg. Date")
</div>
<div class="editor-field">
<div class="input-group date">
#Html.TextBoxFor(model => model.BegDueDate, null, new { #class = "form-control" })
<span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</div>
<div class="col-lg-2 form-group">
<div class="editor-label">
#Html.Label("Due End Date")
</div>
<div class="editor-field">
<div class="input-group date">
#Html.TextBoxFor(model => model.EndDateDate, null, new { #class = "form-control" })
<span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</div>
<div class="col-lg-2 form-group">
<div class="editor-label">
#Html.Label("Gen S/N")
</div>
<div class="editor-field">
<div class="input-group">
#Html.DropDownListFor(model => model.GenSn, DropdownLists.GetGenSns(Convert.ToInt32(Session["CustomerNumber"]), Session["PmType"].ToString(), Session["Whse"].ToString()), new { #class = "form-control" })
</div>
</div>
</div>
<div class="col-lg-2 form-group">
<div class="editor-label">
#Html.Label("PM Name")
</div>
<div class="editor-field">
<div class="input-group">
#Html.DropDownListFor(model => model.PmName, DropdownLists.GetPmNames(Convert.ToInt32(Session["CustomerNumber"]), Session["PmType"].ToString(), Session["Whse"].ToString()), new { #class = "form-control" })
</div>
</div>
</div>
<div class="col-lg-2 form-group">
<div class="editor-label">
#Html.Label("Type")
</div>
<div class="editor-field">
<div class="input-group">
#Html.DropDownListFor(model => model.Type, DropdownLists.GetTypes(), new { #class = "form-control" })
</div>
</div>
</div>
<div class="col-lg-2 form-group">
<div class="editor-label">
</div>
<div class="editor-field">
<input type="submit" value="Search" class="btn btn-success" />
<input type="submit" value="Clear" class="btn btn-danger" />
</div>
</div>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Gen S/N</th>
<th>Loc ID</th>
<th style="width: 140px;">PM Description</th>
<th>City</th>
<th>Next PM Due Dt.</th>
<th>Sched Arrival Dt.</th>
<th style="width: 160px;">Actions</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Pms.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(x => x.Pms[i].FleetTruckNo)
#Html.DisplayFor(x => x.Pms[i].FleetTruckNo)
</td>
<td>
#Html.HiddenFor(x => x.Pms[i].LocationID)
#Html.DisplayFor(x => x.Pms[i].LocationID)
</td>
<td>
#Html.HiddenFor(x => x.Pms[i].Description)
#Html.DisplayFor(x => x.Pms[i].Description)
</td>
<td>
#Html.HiddenFor(x => x.Pms[i].City)
#Html.DisplayFor(x => x.Pms[i].City)
</td>
<td>
<div class="editor-field">
<div class="input-group date">
#Html.TextBoxFor(x => x.Pms[i].NextPMDueDt, "{0:MM/dd/yyyy}", new { #class = "form-control" })
<span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</td>
<td>
<div class="editor-field">
<div class="input-group date">
#Html.TextBoxFor(x => x.Pms[i].EstArrivalDt, "{0:MM/dd/yyyy}", new { #class = "form-control" })
<span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</td>
<td>
<input type="hidden" class="query" value="?customerNumber=#Model.Pms[i].CustNo&fleetTruckNo=#Model.Pms[i].FleetTruckNo&sro=#Model.Pms[i].RepairOrdNo&scheduleId=#Model.Pms[i].ScheduleID" />
<input type="button" class="comments btn btn-primary" value="Comments" />
<input type="button" class="btn btn-success" value="Process" />
</td>
</tr>
}
#for (var i = 0; i < Model.Repairs.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(x => x.Repairs[i].FleetTruckNo)
#Html.DisplayFor(x => x.Repairs[i].FleetTruckNo)
</td>
<td>
#Html.HiddenFor(x => x.Repairs[i].LocationID)
#Html.DisplayFor(x => x.Repairs[i].LocationID)
</td>
<td>
#Html.HiddenFor(x => x.Repairs[i].Description)
#Html.DisplayFor(x => x.Repairs[i].Description)
</td>
<td>
#Html.HiddenFor(x => x.Repairs[i].City)
#Html.DisplayFor(x => x.Repairs[i].City)
</td>
<td>
N/A
</td>
<td>
<div class="editor-field">
<div class="input-group date">
#Html.TextBoxFor(x => x.Repairs[i].ScheduledDt, "{0:MM/dd/yyyy}", new { #class = "form-control" })
<span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</td>
<td>
<input type="hidden" class="query" value="?customerNumber=#Model.Repairs[i].CustNo&fleetTruckNo=#Model.Repairs[i].FleetTruckNo&sro=#Model.Repairs[i].SRO&scheduleId=#Model.Repairs[i].ScheduleID&repair=true" />
<input type="button" class="comments btn btn-primary" value="Comments" />
<input type="button" class="btn btn-success" value="Process" />
</td>
</tr>
}
</tbody>
</table>
}
As you can see in the view I have two for loops going throw Repairs and PMs. Does anyone know why I am getting null values in the POST Action?
add this to you model constructor and see if it helps.
public class CustomPmScheduleGenerator
{
public CustomPmScheduleGenerator()
{
Pms = new List<stp_GetPmSchedulesGenerator_Result>();
Repairs = new List<stp_GetRepairSchedulesGenerator_Result>();
}
public bool Save { get; set; }
etc..
public IList<stp_GetPmSchedulesGenerator_Result> Pms { get; set; }
public IList<stp_GetRepairSchedulesGenerator_Result> Repairs { get; set; }
}
Model binder can create a new instance of your model but the Pms and Repairs objects are null, unless in instantiate them in the constructor
put your for under a if statement
if(Model.Pms != null){
for...
}

HTTPPost returns empty values in my complex model

I have a Complex model in a View, which is editable. In my View, when I click save, the model returned is null or empty, except for the "nri_id" value. I've looked at a bunch of the other posts and tried them all but can't seem to get mine to work. I feel like i'm just missing something simple.....
public partial class nri
{
public nri()
{
tasks = new HashSet<task>();
}
[Key]
public int nri_id { get; set; }
public int ref_number { get; set; }
public int state_id { get; set; }
[UIHint("_taskList")]
public virtual ICollection<task> tasks { get; set; }
}
and a view:
#model NSCEngineering.Models.nri
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>nri</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<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 class="form-group">
#Html.HiddenFor(model => model.nri_id)
<dl class="dl-horizontal">
<dt>
#Html.LabelFor(model => model.state_id)
</dt>
<dd>
#Html.DisplayFor(model => model.state_id)
</dd>
<dt>
#Html.LabelFor(model => model.ref_number)
</dt>
<dd>
#Html.DisplayFor(model => model.ref_number)
</dd>
</dl>
<h4>#Html.LabelFor(model => model.tasks)</h4>
<hr />
#Html.EditorFor(model => model.tasks)
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
and my controller:
// GET: nris/Details/5
[HttpGet]
public async Task<ActionResult> Details(int id = 0)
{
nri n = await _nriRepository.GetNRIByID(id);
return View(n);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(nri nriModel)
{
try
{
if (ModelState.IsValid)
{
_nriRepository.UpdateNRI(nriModel);
_nriRepository.SaveAll();
return View(nriModel);
}
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(nriModel);
}
EDIT:
Changed all the #Html.DisplayFor to #Html.TextBoxFor(model => model.xxxx, new { #readonly = "readonly" })`, and that did the trick.
My Task EditorTemplate is below. I have a Hieriachal table,' category', which groups and displays the tasks accordingly. I'm assuming i'll also need a #Html.HiddenFor(model => model.nri_id) somewhere in there too...:
#model IEnumerable<NSCEngineering.Models.task>
<div class="form-group">
#{var categories = ViewData["AllCategories"] as List<NSCEngineering.Models.category>;}
#for (int i=0; i < (categories.Where(x=>x.parent_category_id ==null)).Count(); i++)
{
<dl>
<dt>#Html.DisplayFor(x => categories[i].category_name)</dt>
#{IEnumerable<NSCEngineering.Models.task> CategoryTasks = Model.Where(z => z.category_id == categories[i].category_id);}
#foreach (var task in CategoryTasks) {
<dd>
#Html.EditorFor(x => task, "_task")
</dd>
}
#*}*#
#for (int j = 0; j < categories[i].Subcategories.Count(); j++ )
{
<dd>
<dl>
<dt>
#Html.DisplayFor(x => categories[i].Subcategories[j].category_name)
</dt>
#{IEnumerable<NSCEngineering.Models.task> subTasks = Model.Where(task => task.category_id == categories[i].Subcategories[j].category_id); }
#foreach (var subtask in subTasks)
{
<dd>
#Html.EditorFor(x => subtask, "_task")
</dd>
}
</dl>
</dd>
}
</dl>
}
and my Task Template:
#model NSCEngineering.Models.task
<tr>
#Html.HiddenFor(model => model.task_id)
<td>
#Html.DisplayFor(model => model.task_name)
</td>
<td>
#Html.DisplayFor(model => model.task_desc)
</td>
<td>
#Html.DropDownListFor(model => model.task_state_id, new SelectList((System.Collections.IEnumerable)ViewData["TaskStates"], "task_state_id", "state"))
</td>
<td>
#Html.DisplayFor(model => model.user_completed)
</td>
<td>
#Html.DisplayFor(model => model.completion_date)
</td>
<td>
#Html.EditorFor(model => model.notes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.notes, "", new { #class = "text-danger" })
</td>
</tr>

Categories