I have the following, abridged for clarity and confidentiality, viewmodels:
public class BoqReviewViewModel
{
public List<BoqReviewItemViewModel> Items { get; set; } = new List<BoqReviewItemViewModel>();
}
public static BoqReviewItemViewModel FromEntity(GTT_BOQ_IMPORT import)
{
var ret = new BoqReviewItemViewModel();
ret.BOQ_IMPORT_ID = import.BOQ_IMPORT_ID;
ret.ITEM_CODE = import.ITEM_CODE;
ret.ITEM_DESC = import.ITEM_DESC;
ret.UNIT = import.UNIT;
ret.QTY = import.QTY;
ret.RATE = import.RATE;
ret.AMOUNT = import.AMOUNT;
ret.ITEM_PAYS = import.ITEM_PAYS;
ret.ITEM_CPA = import.ITEM_CPA;
return ret;
}
public GTT_BOQ_IMPORT ToEntity()
{
var ret = new GTT_BOQ_IMPORT();
ret.BOQ_IMPORT_ID = this.BOQ_IMPORT_ID;
ret.ITEM_CODE = this.ITEM_CODE;
ret.ITEM_DESC = this.ITEM_DESC;
ret.UNIT = this.UNIT;
ret.QTY = this.QTY;
ret.RATE = this.RATE;
ret.AMOUNT = this.AMOUNT;
ret.ITEM_PAYS = this.ITEM_PAYS;
ret.ITEM_CPA = this.ITEM_CPA;
return ret;
}
[Key]
public Guid BOQ_IMPORT_ID;
public string ITEM_CODE { get; set; }
public string ITEM_DESC { get; set; }
public string UNIT { get; set; }
public decimal? QTY { get; set; }
public decimal? RATE { get; set; }
public decimal? AMOUNT { get; set; }
public bool? ITEM_PAYS { get; set; }
public bool? ITEM_CPA { get; set; }
public bool IsFlagged { get; set; }
}
Then I have the following GET action for an index type view:
[HttpGet]
public ActionResult BoqReview()
{
var model = new BoqReviewViewModel();
model.CONTRACT_ID = Guid.NewGuid();
model.PROJECT_ID = Guid.NewGuid();
model.Items = GetItems().ToList();
return View(model);
}
Where GetItems is a method that returns an IEnumerable with three fully populated item level viewmodels. When I startup the app, I see a properly populated table with 3 rows with all columns properly populated.
This is the view:
#using (Html.BeginForm("BoqReview", "Boq", FormMethod.Post))
{
<table class="table">
<tr>
<th>
</th>
<th class="col-header">
Item Code
</th>
<th class="col-header">
Item Description
</th>
<th>
Unit
</th>
<th>
Quantity
</th>
<th>
Rate
</th>
<th>
Amount
</th>
<th>
Item Pays
</th>
<th>
Item CPA
</th>
<th>
</th>
</tr>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(model => model.Items[i].BOQ_IMPORT_ID)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].ITEM_CODE)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_CODE)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].ITEM_DESC)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_DESC)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].UNIT, new {style = "width: 120px;"})
#Html.ValidationMessageFor(model => Model.Items[i].UNIT)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].QTY)
#Html.ValidationMessageFor(model => Model.Items[i].QTY)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].RATE)
#Html.ValidationMessageFor(model => Model.Items[i].RATE)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].AMOUNT)
#Html.ValidationMessageFor(model => Model.Items[i].AMOUNT)
</td>
<td>
#Html.DropDownListFor(model => Model.Items[i].ITEM_PAYS, Model.YesNoSelectList)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_PAYS)
</td>
<td>
#Html.DropDownListFor(model => Model.Items[i].ITEM_CPA, Model.YesNoSelectList)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_CPA)
</td>
<td>
#Html.CheckBoxFor(model => Model.Items[i].IsFlagged)
</td>
</tr>
}
<tr>
<td colspan="4">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Everything looks perfect, yet when I submit the form:
[HttpPost]
public ActionResult BoqReview(BoqReviewViewModel model)
{
using (var db = new AppDbContext())
{
foreach (var modelItem in model.Items)
{
var updated = modelItem.ToEntity();
db.GttBoqImports.AddOrUpdate(updated);
}
db.SaveChanges();
}
return RedirectToAction("BoqReview");
}
The BOQ_IMPORT_ID field on each item viewmodel is an empty GUID, yet the displayed table shows a proper GUID. All other properties of the item level viewmodels are still populated.
What could be wrong that just this BOQ_IMPORT_ID is not properly model bound? When I debug the network call in Chrome tools, it shows a proper GUID value for each BOQ_IMPORT_ID in the form data posted:
Items[0].BOQ_IMPORT_ID: 610d9a12-920f-43aa-89bc-08b68067a462
Items[0].ITEM_CODE: AA1
Items[0].ITEM_DESC: Some A's and a 1
Items[0].UNIT: kg
Items[0].QTY: 200
...
JUST IN: Renaming the VM property in that class and in the view solved the whole problem. Maybe, despite several VS restarts, a machine restart mighty have somehow helped. This smells of temp files or some caching somewhere.
Related
I'm getting myself acquainted with ASP.NET MVC but i'm running into something probably trivial. I have a model called ToDoList, this is a complex type with a list of ToDoItems:
public class ToDoList
{
public Guid Id {get;set;}
public string Name { get; set; }
public virtual ICollection<ToDoItem> Items {get;set;}
}
public class ToDoItem
{
public int Id { get; set; }
public string Task { get; set; }
public bool IsDone { get; set; }
public virtual ToDoList ToDoList { get; set; }
}
My Details page with form looks like this:
#model DataLayer.TomTest.Entities.ToDoList
<h2>#Model.Name</h2>
#using (#Html.BeginForm())
{
#Html.AntiForgeryToken()
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Items.First().Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().Task)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().IsDone)
</th>
</tr>
#foreach (var toDoItem in Model.Items)
{
<tr>
<td>
#toDoItem.Id
</td>
<td>
#Html.EditorFor(model => toDoItem.Task)
</td>
<td>
#Html.EditorFor(model => toDoItem.IsDone, new {htmlAttributes = new {#Style = "margin-left: 10px;"}})
</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-default"/>
}
And this is the method it posts to:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Details([Bind(Include = "Id,Name,Items")] ToDoList todoList)
{
if (ModelState.IsValid)
{
_context.Entry(todoList).State = EntityState.Modified;
await _context.SaveChangesAsync();
return View();
}
return View();
}
As you can see I included the [Bind] attribute as I read somewhere that would ensure i get the correct properties passed. When I debug this however, only the Id property is filled the rest remains null.
What can I do to fix this? Is it a mistake in the View? Or is it possible Entity Framework isn't setup correctly?
Thanks in advance for your help.
Model binding to a list doesn't work with a foreach; you need to use a for loop instead.
You'll also need hidden inputs for any properties which don't have editors within the loop.
#for (int index = 0; index < Model.Items.Count; index++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[index].Id)
#Model.Items[index].Id
</td>
<td>
#Html.EditorFor(m => m.Items[index].Task)
</td>
<td>
#Html.EditorFor(m => m.Items[index].IsDone, new { htmlAttributes = new { #Style = "margin-left: 10px;" } })
</td>
</tr>
}
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog
Any help would be great. I have recently started a project in MVC to store some client details, I created the model, controller and then right-clicked Add View. I am relatively new and I am trying to show the results of a query in a list view but getting the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType148[System.String,System.String,System.Nullable1[System.DateTime],System.String,System.String,System.String,System.String,System.String]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[cc_proj.ViewModels.HomeDashboardViewModel]'.
Controller:
[HttpGet]
public ActionResult Dash()
{
using (var context = new CRM_businessdevelopmentEntities())
{
var data =
(from Clients in context.Clients
join Sizes in context.Sizes on new { SizeID = (int)Clients.SizeID } equals new { SizeID = Sizes.SizeID }
join CampaignTypes in context.CampaignTypes on new { CampaignTypeID = (int)Clients.CampaignTypeID } equals new { CampaignTypeID = CampaignTypes.CampaignTypeID }
join Countrys in context.Countrys on new { CountryID = (int)Clients.CountryID } equals new { CountryID = Countrys.CountryID }
join Contacts in context.Contacts on new { ContactID = (int)Clients.ContactID } equals new { ContactID = Contacts.ContactID }
join Stages in context.Stages on new { StageID = (int)Clients.StageID } equals new { StageID = Stages.StageID }
where
Clients.Active == "True"
orderby
Clients.DateAdded descending
select new
{
Stage = Stages.Description,
Name = Clients.Name,
DateAdded = Clients.DateAdded,
Contact = Contacts.Contact,
Location = Countrys.Country,
CampaignType = CampaignTypes.Description,
Size = Sizes.Description,
Notes = Clients.Notes
}).ToList();
return View(data);
}
}
ViewModel:
public class HomeDashboardViewModel
{
[Key]
public int ClientID { get; set; }
public string Stage { get; set; }
public string Name { get; set; }
public DateTime DateAdded { get; set; }
public string Contact { get; set; }
public string Location { get; set; }
public string CampaignType { get; set; }
public string Size { get; set; }
public string Notes { get; set; }
public string Active { get; set; }
}
View:
#model IEnumerable<Business_Development_CRM.ViewModels.HomeDashboardViewModel>
#{
ViewBag.Title = "Dash";
}
<h2>Dash</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Stage)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.DateAdded)
</th>
<th>
#Html.DisplayNameFor(model => model.Contact)
</th>
<th>
#Html.DisplayNameFor(model => model.Location)
</th>
<th>
#Html.DisplayNameFor(model => model.CampaignType)
</th>
<th>
#Html.DisplayNameFor(model => model.Size)
</th>
<th>
#Html.DisplayNameFor(model => model.Notes)
</th>
<th>
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Stage)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateAdded)
</td>
<td>
#Html.DisplayFor(modelItem => item.Contact)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location)
</td>
<td>
#Html.DisplayFor(modelItem => item.CampaignType)
</td>
<td>
#Html.DisplayFor(modelItem => item.Size)
</td>
<td>
#Html.DisplayFor(modelItem => item.Notes)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ClientID }) |
#Html.ActionLink("Details", "Details", new { id=item.ClientID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ClientID })
</td>
</tr>
}
</table>
try to fix the query, instead of a list of anonymous objects, create a list of ViewModel objects
var data =
(from Clients in context.Clients
.....
select new HomeDashboardViewModel
{
Stage = Stages.Description,
.....
}).ToList();
I am having difficulty getting my Index view to display the values I have from the SQL database.
The main issue is that I cannot use #foreach (var item in Model) {... because my table is not created as Enumerable (I think). I run into the error message System.NullReferenceException: 'Object reference not set to an instance of an object.' pointing at Model in the foreach statement expression.
I am wondering if I need to set my table up as an Enumerable list, then display each item. Or if I am missing something with regards to my Index view. Or maybe I need to pass something through the Index return View()?
Here is the Image Model:
namespace Uploadimage.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
public partial class Image
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Phone { get; set; }
[Required]
[RegularExpression(#"[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email doesn't look like a valid email address.")]
public string Email { get; set; }
[Compare("Email", ErrorMessage = "Emails do not match.")]
public string ConfirmEmail { get; set; }
[DisplayName("Upload File")]
public string ImagePath { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
}
}
Here is the controller:
public class ImageController : Controller
{
[HttpGet]
public ActionResult Add()
{
return View();
}
[HttpPost]
public ActionResult Add(Image imageModel)
{
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.ImagePath = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
using(LoginDBEntities db = new LoginDBEntities())
{
db.Images.Add(imageModel);
db.SaveChanges();
}
ModelState.Clear();
return View();
}
[HttpGet]
public ActionResult View(int id)
{
Image imageModel = new Image();
using (LoginDBEntities db = new LoginDBEntities())
{
imageModel = db.Images.Where(x => x.Id == id).FirstOrDefault();
}
return View(imageModel);
}
public ActionResult Index()
{
return View();
}
}
And lastly, my Index View:
#model IEnumerable<Uploadimage.Models.Image>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
I have tried thoroughly looking this up but have found things that don't specifically apply to me.
Or I don't know what to look up.
Two things:
Your Index controller action will need to load the images to pass to the view. At a minimum to serve a collection of images as the view's "model":
public ActionResult Index()
{
using (LoginDBEntities db = new LoginDBEntities())
{
var images = db.Images.ToList();
return View(images);
}
}
Then in the view you will need to check whether you actually get any results, then extract your labels from the first result if there are any, or display a suitable message if there are no images:
<!-- ... -->
#if (Model.Any())
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).LastName)
</th>
...
</tr>
#for (int count = 0; count < Model.Count; count++) {
<tr>
<td>
#Html.DisplayFor(model => model.ElementAt(count).FirstName)
</td>
<td>
#Html.DisplayFor(model => model.ElementAt(count).LastName)
</td>
...
<td>
#Html.ActionLink("Edit", "Edit", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Details", "Details", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Delete", "Delete", new { id=#Model.ElementAt(count).Id })
</td>
</tr>
}
</table>
}
else
{
<p> No Images. </p>
}
The above is written from memory so it will likely have some syntactic issues but it should point you in the right direction.
Typically when working with collections like search results I define a view model class for the results page that itself contains the collection of results. The page's model becomes that wrapper view model which can contain details about the page (such as things like current search criteria, lookup values for searches, etc.) along with the collection of results. (typically a PagedList to support pagination)
I.e.
[Serializable]
public class ImageIndexViewModel
{
public string NameSearchString { get; set; }
public ICollection<ImageViewModel> Results { get; set; } = new List<ImageViewModel>();
}
Where ImageViewModel is a serializable POCO view model to represent just the details about images that the view will display. Often the views don't need everything about a data row, and in cases where the data row has navigation properties and extra fields we don't need to display, serializing entities results in lazy load calls or simply sending a lot of extra data that isn't needed.
You don't have anything in your Index method which gets a list of entities.
Your Index method in your controller should look something like:
public ActionResult Index()
{
var model = db.Images.ToList();
return View(model);
}
I have two models "Computer" and "Computer Detail." From the Computer's Index page the user can click "View Details" for a given record which redirects to the Index of the Computer Detail and fetches all the records associated with that given "Computer ID" using the query string - That works fine
My problem is I want to have a "Create" link that carries this "Computer ID" (shown in view) and populates the Computer ID field on the Create form.
I used Model.First().ComputerID that worked to run the code with some test records but of course it doesn't work if records for the ComputerID is null.
View
#modelIEnumerable<MyApp.Models.ComputerDetail>
#{
ViewBag.Title = "Index";
}
<h2 class ="page-header">Computer Details</h2>
<div class ="col-lg-12">
<p>
#Html.ActionLink("Create New", "Create", "ComputerDetail", new {id = Model.First().ComputerID}, new { #class = "btn btn-default"})
</p>
<div class="panel panel-default">
<div class ="panel-body">
<table class ="table table-striped" id="dtaTable">
<thead class ="dataTableHead">
<tr>
<th>
#Html.DisplayNameFor(model => model.ComputerID)
</th>
<th>
#Html.DisplayNameFor(model => model.EmployeeID)
</th>
<th>
#Html.DisplayNameFor(model => model.StartDate)
</th>
<th>
#Html.DisplayNameFor(model => model.EndDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Comments)
</th>
<th>Actions</th>
</tr>
</thead>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ComputerID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Employee.FullName)
</td>
<td>
#Html.DisplayFor(modelItem => item.StartDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.EndDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comments)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
</div>
</div>
</div>
Controller
public ActionResult Index(int id)
{
var _FindComputerID = GetComputerDetails(id);
return View(_FindComputerID);
}
private List<ComputerDetail>GetComputerDetails(int id)
{
var FindComputerID = db.ComputerDetails.Where(cd => cd.ComputerID == id).Include
(cd => cd.Employee).OrderByDescending(cd => cd.ID);
return FindComputerID.ToList();
}
[HttpGet]
public ActionResult Create(int id)
{
ComputerDetail computerdetail = new ComputerDetail ();
computerdetail.ComputerID = id;
return View(computerdetail);
}
Model
public class ComputerDetail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required(ErrorMessage = "Please enter service tag")]
[Display(Name = "Service Tag")]
public int ComputerID { get; set; }
[Required(ErrorMessage = "Please select employee name")]
[Display(Name = "Employee Name")]
public int EmployeeID { get; set; }
[Required(ErrorMessage = "Please enter date")]
[Display(Name = "Date Bought")]
[DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[Display(Name = "Date Bought")]
[DataType(DataType.Date)]
public DateTime? EndDate { get; set; }
public string Comments { get; set; }
//references
public virtual Assets.Computer Computer { get; set; }
public virtual Employee Employee { get; set; }
}
Two thing are happening here:
In the first place, if your view needs to get the ComputerID you may want to reflect this field in the ViewModel and not magically get it from the first item in the list.
public class ComputerDetailsViewModel
{
public int ComputerID {get; set;} // populate in the action directly
public IEnumerable<MyApp.Models.ComputerDetail> Items {get; set;}
}
You really want to go that route, you should use the nullable operator for better error handling figure it out what happens when the ComputerID is null.
#Html.ActionLink("Create New", "Create", "ComputerDetail", new {id = Model.FirstOrDefault()?.ComputerID ?? 0 /*Null case*/}, new { #class = "btn btn-default"})
Hope this help!
I would like to save users steps and have each row in my MVC index view be a form.
I am using the HTML.Begin form inside the foreach to build the individual forms. The page renders without errors and the form submits without problems, but I can not bind to a model inside the controller - thus all the submitted data is lost.
I have validated that the form values are there to begin with when the form is submitted: item.Completed=true&item.Completed=false&item.Comment=HELLO+WORLD+&item.FinalizeStepId=1&item.StepId=1, but the controller does not accept them and the FinalizeStepViewModel object is created with null values.
So how do I get the form to pass back the data correctly?
This might be my second question ever on Stackoverflow, so let me know what additional information I might need to add.
Thanks.
=== Model =====
public class FinalizeStepViewModel
{
public int FinalizeStepId { get; set; }
// foreign key from Step table
public int StepId { get; set; }
// name of taks from Step table
public string StepDesc { get; set; }
[DisplayName("Review Data")]
public string ReviewFormulaValue { get; set; }
[Required]
public bool Completed { get; set; }
[DisplayName("Fiscal Year")]
public int FiscalYear { get; set; }
// Period for the adjustment
[Required]
public int Period { get; set; }
[Required]
public string UserID { get; set; }
[Required]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
[Required]
[DisplayName("Created At")]
public DateTime CreatedAt { get; set; }
public string Comment { get; set; }
==== View ==========
#model IEnumerable
#{
ViewBag.Title = "Index";
// is everything completed, if yes => enabled
string alldone = "enabled";
}
<h2>Finalize Checklist</h2>
<table class="table">
<tr>
<th>
Completed
</th>
<th>
Finalized Task
</th>
<th>
Review Data
</th>
<th>
#Html.DisplayNameFor(model => model.Comment)
</th>
<th></th>
<th></th>
#*<th>
#Html.DisplayNameFor(model => model.FiscalYear)
</th>
<th>
#Html.DisplayNameFor(model => model.Period)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedBy)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedAt)
</th>
<th>
#Html.DisplayNameFor(model => model.UserID)
</th>*#
<th></th>
</tr>
#foreach (var item in Model)
{
//<form action="/FinalizeSteps/Checklist/" method="post">
//#using (Html.BeginForm("Login", "Account", FormMethod.Post))
//// <form action="/Account/Login" action="post">
using (Html.BeginForm("EditFromChecklist", "FinalizeSteps", FormMethod.Post, new { finalizeStepPassed = Model }))
{
<tr>
<td>
<div class="form-group" style="text-align: center; vertical-align: text-top;">
<div class="checkbox">
#Html.EditorFor(modelItem => item.Completed)
#if (item.Completed == false) { alldone = "disabled"; }
</div>
</div>
</td>
<td>
<h4>#Html.DisplayFor(modelItem => item.StepDesc)</h4>
</td>
<td style="text-align: center;">
#Html.DisplayFor(modelItem => item.ReviewFormulaValue)
</td>
<td>
<div class="form-group" style="width: 300px;">
#Html.EditorFor(modelItem => item.Comment, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.Comment, "", new { #class = "text-danger" })
</div>
</td>
<td>
<div class="form-group">
#Html.EditorFor(modelItem => item.FinalizeStepId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.FinalizeStepId, "", new { #class = "text-danger" })
</div>
</td>
<td>
<div class="form-group">
#Html.EditorFor(modelItem => item.StepId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.FinalizeStepId, "", new { #class = "text-danger" })
</div>
</td>
#*<td>
#Html.DisplayFor(modelItem => item.FiscalYear)
</td>
<td>
#Html.DisplayFor(modelItem => item.Period)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreatedBy)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreatedAt)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserID)
</td>*#
<td>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
#Html.ActionLink("Save", "EditFromChecklist", new { FinalizeStepId = item.FinalizeStepId, StepId = item.StepId, Completed = item.Completed, Comment = item.Comment })
#*#Html.ActionLink("Edit", "Edit", new { id = item.FinalizeStepId }) |
#Html.ActionLink("Details", "Details", new { id = item.FinalizeStepId }) |
#Html.ActionLink("Delete", "Delete", new { id = item.FinalizeStepId })*#
</td>
</tr>
}
}
</table>
=== Controller Method ====
[HttpPost]
public ActionResult EditFromChecklist([Bind(Include = "FinalizeStepId,StepId,Completed,Comment")] FinalizeStepViewModel finalizeStepPassed)
{
// Do we have a FinalizeStepId?
if (finalizeStepPassed.FinalizeStepId != 0)
{
// Yes, this is an edit
...
Change your EditFromChecklist action's parameter from finalizeStepPassed to item.
Or you could use a partial view to submit your data.
_FinalizeStepPartial.cshtml
#model FinalizeStepViewModel
using (Html.BeginForm("EditFromChecklist", "FinalizeSteps"))
{
#Html.EditorFor(model => model.Completed)
// rest of your form
}
and in the main view inside of loop call the partial
#foreach (var item in Model)
{
#Html.Partial("_FinalizeStepPartial",item)
}
You should bind to a IList instead of IEnumerable, and instead of
#foreach (var item in Model)
{
#Html.EditorFor(modelItem => item.Completed)
}
Use this syntax
#for( int i=0; i < Model.Count; i++ )
{
#Html.EditorFor(modelItem => Model[i].Completed)
}
Here is an earlier topic that also discussed this: How to pass IEnumerable list to controller in MVC including checkbox state?