I need to bind a List inside a nested class to my ActionMethod.
Now, on calling the ActionMethod (look below), ActionItemList is null
Unfortunately i can't move the List into the main Model.
This is my main Model:
public class StateViewModel
{
public EmergencyOperationActionListModel ActionListModel { get; set; }
public EmergencyInfoModel InfoModel
public EmergencyInfoCauseListModel CauseListModel { get; set; }
}
and the nested one:
public class EmergencyInterventiActionListModel
{
public string Firefighters { get; set; }
public string ExternalAssistance { get; set; }
public string PlacesDescription { get; set; }
public List<ActionItemModel> ActionItemList { get; set; }
}
The view:
#model Emergencies.Models.StatoViewModel
//...code code code
#using (Html.BeginForm("EditOps", "Operations")
{
<table class="table table-responsive table-hover" style="margin-bottom: 0px;">
<thead class="headOperations">
<tr>
<td>
CompanyName
</td>
<td>
Workers
</td>
<td>
Due Date
</td>
<td>
Start Date
</td>
<td>
End Date
</td>
<td>
Action
</td>
</tr>
</thead>
#if (Model.ActionListModel != null)
{
for (int i = 0; i < Model.ActionListModel.ActionItemList.Count(); i++)
{
<tr>
<td>
#Html.HiddenFor(m=>m.ActionListModel.ActionItemList[i].Id)
#Html.EditorFor(m => m.ActionListModel.ActionItemList[i].CompanyName, new { htmlAttributes = new { id = "companyEdit_" + Model.ActionListModel.ActionItemList[i].Id, #class = "editBoxForInterventi" } })
</td>
<td>
#Html.EditorFor(m => m.ActionListModel.ActionItemList[i].NumberOfWorkers, new { htmlAttributes = new { id = "workersEdit_" + Model.ActionListModel.ActionItemList[i].Id, style = "width:40px", #class = "editBoxForInterventi" } })
</td>
<td>
<div class="input-group date" id="duedateCalendar_#Model.ActionListModel.ActionItemList[i].Id">
#Html.EditorFor(m => m.ActionListModel.ActionItemList[i].DueDate, new { htmlAttributes = new { onclick = "CalendarDue('" + Model.ActionListModel.ActionItemList[i].Id + "')", id = "duedateEdit_" + Model.ActionListModel.ActionItemList[i].Id, #class = "editBoxForInterventi form-control dueDateCalendar", #readonly = "readonly" } })
</div>
</td>
<td>
<div class="input-group date" id="starttimeCalendar_#Model.ActionListModel.ActionItemList[i].Id">
#Html.EditorFor(m => m.ActionListModel.ActionItemList[i].StartTime, new { htmlAttributes = new { id = "starttimeEdit_" + Model.ActionListModel.ActionItemList[i].Id, #class = "editBoxForInterventi form-control starttimeCalendar", #readonly = "readonly" } })
</div>
</td>
<td>
<div class="input-group date" id="endtimeCalendar_#Model.ActionListModel.ActionItemList[i].Id">
#Html.EditorFor(m => m.ActionListModel.ActionItemList[i].EndTime, new { htmlAttributes = new { id = "endtimeEdit_" + Model.ActionListModel.ActionItemList[i].Id, #class = "editBoxForInterventi form-control endtimeCalendar", #readonly = "readonly" } })
</div>
</td>
<td></td>
</tr>
}
}
</table>
<button type="submit" class="submit-with-icon btn btn-flussi-add" name="doButton" value="save">
<span class="glyphicon glyphicon-pencil"></span>
</button>
}
//code code code...
And finally the controller:
public ActionResult EditOps( List<ActionItemModel> ActionItemList )
{
//code
}
Just change your EditOps method signature to have the below.
public ActionResult EditOps(StateViewModel modelPosted)
{
//access modelPosted here.
}
The whole model is serialized as per the #model directive in your view.
Related
I am new to ASP.NET MVC. I have a parent view and a partial view, both using different models. My concern is when I submit the page, the partial view data also should pass to the parent view HTTP Post method. I had created a property in the parent view model to get the data from the partial view model. But when I submit the page, I am getting null. any help would be appreciated
Parent view caseDetails.cshtml:
#model EMSD.Module.Case.CPN.Model.CPNDetailViewModel
#{
ViewBag.Title = "_CPNCaseDetail";
}
<table class="table table-striped">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<tr>
<td class="leftheaderform">
#Html.LabelFor(model => model.CPN_CAT)
<span style="color:red">*</span>
</td>
<td class="rightdetailform" colspan="3">
#Html.DropDownListFor(model => model.CPN_CAT, new SelectList(Model.InformedCat, "ID", "Name"), "--Select--", new { #class = "form-control form-control-sm col-3" })
#Html.ValidationMessageFor(model => model.CPN_CAT, "", new { #class = "text-danger" })
</td>
</tr>
<tr>
<td class="leftheaderform">
#Html.LabelFor(model => model.CPN_CAT_RMK)
</td>
<td class="rightdetailform" colspan="3">
#Html.TextAreaFor(model => model.CPN_CAT_RMK, new { htmlAttributes = new { #class = "form-control form-control-sm" }, rows = 2, style = "width: 100%; max-width: 100%;" })
#Html.ValidationMessageFor(model => model.CPN_CAT_RMK, "", new { #class = "text-danger" })
</td>
</tr>
*used HTML.partial for calling partial view*
#Html.Partial("~/Views/Shared/Address.cshtml", Model.Address)
</table>
Parent view model:
public class CPNDetailViewModel
{
[DisplayName("Informed Category")]
public string CPN_CAT { get; set; }
[DisplayName("Remarks ")]
public string CPN_CAT_RMK { get; set; }
// property for getting data from partial view
public UpdateGasSupplierViewModel Address { get; set; }
}
Partial view Address.chtml:
#model EMSD.Module.Misc.Model.UpdateGasSupplierViewModel
<table class="table table-striped">
<tr>
<td><font color="blue">Search Address</font></td>
<td colspan="4"> <input id="FreeEnglishAddressText" class="form-control" /></td>
<td><button type="button" onclick="callAPI()" class="btn btn-outline-primary form-control">Search</button></td>
</tr>
<tr>
<td>
Flat
</td>
<td>
#Html.DropDownListFor(model => model.GSC_ENG_FT, new SelectList(Model.FlatList, "ID", "Name"), "--Select--", new { #class = "form-control" })
</td>
<td>
#Html.EditorFor(model => model.GSC_ENG_FT_2, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
</table>
Partial view model:
namespace EMSD.Module.Misc.Model
{
public class UpdateGasSupplierViewModel
{
public string GSC_ID { get; set; }
public string GSC_COY_ENAME { get; set; }
}
}
Parent controller method:
[HttpPost]
public ActionResult _CPNCaseDetail(CPNDetailViewModel model)
{
string Post = Session["user_post"].ToString();
if (ModelState.IsValid)
{
cPNCaseService.Save(model);
}
return RedirectToAction("Case", "Case", new { Id = model.CASE_ID, Id2 = queueId, Id3 = "", Id4 = "Y" });
}
You need to use Templated helpers
Templated helpers are different than partials in that special contextual information from the parent is passed down to the child as long as we’re using the Html.EditorXyz() HtmlHelper methods.
Check This
I have a problem with updating ViewModel by PartialView. PartialView includes Devices along with the location that varies depending on the DropDownList selected by the Customer. (At the bottom I present sample screenshot). The problem is that after accepting the submit button the property Devices in the ViewModel(FiscalizationViewModel) is not updating. Here are examples of models. I'm not sure I'm trying to solve this problem properly.
namespace TestMVC.ViewModels
{
public class FiscalizationViewModel
{
public int CustomerId { get; set; }
public string FiscalizationDate { get; set; }
public List<DevicesToFiscalizationViewModel> Devices { get; set; }
public FiscalizationViewModel()
{
Devices = new List<DevicesToFiscalizationViewModel>();
}
public IEnumerable<DevicesToLocalization> GetSelectedIds()
{
return (from d in Devices where d.Selected select new DevicesToLocalization() { DeviceId = d.DeviceId, LocalizationId = d.LocalizationId }).ToList();
}
}
public class DevicesToFiscalizationViewModel
{
public int DeviceId { get; set; }
public string DeviceName { get; set; }
public bool Selected { get; set; }
public string SerialNumber { get; set; }
public int LocalizationId { get; set; }
public IEnumerable<Localization> Localizations { get; set; }
public DevicesToFiscalizationViewModel()
{
Localizations = new List<Localization>();
}
}
}
Here is the method that is called by the Customer DropDownList event
public PartialViewResult CustomerChanged(int CustomerId)
{
var localizations = db.Localizations.Where(i => i.CustomerId == CustomerId).ToList();
var devicesToFsc = (from d in db.Devices
select new DevicesToFiscalizationViewModel()
{
DeviceId = d.DeviceId,
DeviceName = d.Name,
SerialNumber = d.SerialNumber,
}).ToList();
foreach (var item in devicesToFsc)
{
item.Localizations = localizations;
}
return PartialView("~/Views/Fiscalizations/EditorTemplates/DevicesToFiscalizationViewModel.cshtml", devicesToFsc);
//--------------------------------
$("#customerChanged").on("change", function () {
$.ajax(
{
url: '/Fiscalizations/CustomerChanged?CustomerId=' + $(this).val(),
type: 'GET',
data: "",
contentType: 'application/json; charset=utf-8',
success: function (data) {
$("#devicesToFiscalization").html(data);
}
});
});
This is little partial of Views (Fiscalization create view)
#model TestMVC.ViewModels.FiscalizationViewModel
<table class="table" id="devicesToFiscalization">
<thead>
...
</thead>
#Html.Partial("~/Views/Fiscalizations/EditorTemplates/DevicesToFiscalizationViewModel.cshtml", Model.Devices)
</table>
PartialView:
#model IEnumerable<TestMVC.ViewModels.DevicesToFiscalizationViewModel>
#foreach(var item in Model)
{
<tbody>
<tr>
<td style="text-align:center">
<div class="checkbox">
#Html.EditorFor(m => item.Selected)
</div>
</td>
<td>
#Html.DisplayFor(m => item.DeviceName)
</td>
<td>
#Html.DisplayFor(m => item.SerialNumber)
</td>
<td>
#Html.DropDownList("LocalizationId", new SelectList(item.Localizations, "LocalizationId", "Name"), "Select", htmlAttributes: new { #class = "form-control", style = "width: 200px;" })
</td>
<td>
#Html.HiddenFor(m => item.DeviceId)
</td>
</tr>
</tbody>
Here is a screenshot of how it looks
click
and here is screenshot from debugger with bad result
bad
Based on my understating from your views, your issues is using different models for main view and partial view. You should use exactly the same model in both the model binding could update the model on server side. keep both models TestMVC.ViewModels.FiscalizationViewModel or IEnumerable<TestMVC.ViewModels.DevicesToFiscalizationViewModel>
It works :) Below I show how it looks after changes
main view:
#model TestMVC.ViewModels.FiscalizationViewModel
<div id="devicesToFiscalization">
#Html.Partial("~/Views/Fiscalizations/EditorTemplates/DevicesToFiscalizationViewModel.cshtml", Model)
</div>
Partial view:
#model TestMVC.ViewModels.FiscalizationViewModel
<table class="table">
<thead>
<tr>
<th>
Select
</th>
<th>
Name
</th>
<th>
Serial number
</th>
<th>
Localization
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Devices.Count; i++)
{
<tr>
<td style="text-align:center">
<div class="checkbox">
#Html.EditorFor(m => m.Devices[i].Selected)
</div>
</td>
<td>
#Html.DisplayFor(m => m.Devices[i].DeviceName)
</td>
<td>
#Html.DisplayFor(m => m.Devices[i].SerialNumber)
</td>
<td>
#Html.DropDownListFor(m => m.Devices[i].LocalizationId, new SelectList(Model.Devices[i].Localizations, "LocalizationId", "Name"), "Select", htmlAttributes: new { #class = "form-control", style = "width: 200px;" })
</td>
<td>
#Html.HiddenFor(m => m.Devices[i].DeviceId)
</td>
</tr>
}
</tbody>
and CustomerChanged method:
public PartialViewResult CustomerChanged(int CustomerId)
{
var localizations = db.Localizations.Where(i => i.CustomerId == CustomerId).ToList();
var Devices = (from d in db.Devices
select new DevicesToFiscalizationViewModel()
{
DeviceId = d.DeviceId,
DeviceName = d.Name,
SerialNumber = d.SerialNumber,
}).ToList();
foreach (var item in Devices)
{
item.Localizations = localizations;
}
var fsc = new FiscalizationViewModel();
fsc.Devices = Devices;
return PartialView("~/Views/Fiscalizations/EditorTemplates/DevicesToFiscalizationViewModel.cshtml", fsc);
}
========================================================================
I'm trying to write this using EditorFor but I have a problem with correctly writing the CustomerChanged method that returns a list of Devices and not expecting this EditorTemplate that looks as follows:
#model TestMVC.ViewModels.DevicesToFiscalizationViewModel
<tr>
<td style="text-align:center">
<div class="checkbox">
#Html.EditorFor(m => m.Selected)
</div>
</td>
<td>
#Html.DisplayFor(m => m.DeviceName)
</td>
<td>
#Html.DisplayFor(m => m.SerialNumber)
</td>
<td>
#Html.DropDownListFor(m => m.LocalizationId, new SelectList(Model.Localizations, "LocalizationId", "Name"), "Select", htmlAttributes: new { #class = "form-control", style = "width: 200px;" })
</td>
<td>
#Html.HiddenFor(m => m.DeviceId)
</td>
</tr>
Main View:
<table class="table" id="devicesToFiscalization">
...
<tbody>
#Html.EditorFor(m => m.Devices)
</tbody>
</table>
I am writing a web page with MVC and Entity Framework.
I have an order with line items attached and want to return a complex object to the controller for processing.
I have now included all the code.
My view:
#model BCMManci.ViewModels.OrderCreateGroup
#{
ViewBag.Title = "Create";
}
<h2>New Order</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h4>#Html.DisplayFor(model => model.Order.Customer.FullName)</h4>
<table>
<tr>
<td><b>Order Date:</b> #Html.DisplayFor(model => model.Order.OrderDate)</td>
<td><b>Status:</b> #Html.DisplayFor(model => model.Order.OrderStatus.OrderStatusName)</td>
</tr>
<tr>
<td colspan="2">
<b>Notes</b>
#Html.EditorFor(model => model.Order.Notes, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
</table>
#Html.ValidationMessageFor(model => model.Order.Notes, "", new { #class = "text-danger" })
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<table class="table table-striped table-hover">
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Discount</td>
<td>Total</td>
<td>Quantity</td>
</tr>
</thead>
<tbody>
#foreach (var product in Model.ProductWithPrices)
{
<tr>
<td>
#Html.DisplayFor(modelItem => product.ProductName)
</td>
<td>
#Html.DisplayFor(modelItem => product.SellingPrice)
</td>
<td>
#Html.DisplayFor(modelItem => product.DiscountPrice)
</td>
<td>
#Html.DisplayFor(modelItem => product.TotalPrice)
</td>
<td>
#Html.EditorFor(modelItem => product.Quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Create" class="btn btn-default" />
}
<div class="btn btn-danger">
#Html.ActionLink("Cancel", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Order,ProductWithPrices,Order.Note,product.Quantity")] OrderCreateGroup order)
{
try
{
if (ModelState.IsValid)
{
db.Orders.Add(order.Order);
foreach (var orderItem in order.ProductWithPrices.Select(item => new OrderItem
{
OrderId = order.Order.OrderId,
ProductId = item.ProductId,
Quantity = item.Quantity,
ItemPrice = item.SellingPrice,
ItemDiscount = item.DiscountPrice,
ItemTotal = item.TotalPrice
}))
{
db.OrderItems.Add(orderItem);
}
db.SaveChanges();
return RedirectToAction("ConfirmOrder", new {id = order.Order.OrderId});
}
}
catch (DataException /* dex */)
{
//TODO: Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
ViewBag.Products = db.Products.Where(model => model.IsActive == true);
PopulateDropdownLists();
return View(order);
}
Data Source:
public class OrderCreateGroup
{
public OrderCreateGroup()
{
ProductWithPrices = new List<ProductWithPrice>();
}
public Order Order { get; set; }
public ICollection<ProductWithPrice> ProductWithPrices { get; set; }
}
public class ProductWithPrice : Product
{
public decimal SellingPrice { get; set; }
public decimal DiscountPrice { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice { get; set; }
}
However, the values that are entered on the form are not being passed, through. So I can't access them in the controller. The 'productWithPrices' collection is null although there is Data in it on the web page.
I have tried making it asyc and also tried changing the ActionLink button like below but it didn't get to the controller.
#Html.ActionLink("Create", "Create", "Orders", new { orderCreateGoup = Model }, null)
This is the controller but it now doesn't make sense as the parameter passed in the datasource for the page.
public ActionResult Create(OrderCreateGroup orderCreateGoup)
Please, can you give me direction on the best way of doing this?
In your OrderCreateGroup class initialize the collection to an empty list.
public class OrderCreateGroup
{
public OrderCreateGroup()
{
ProductWithPrices = new List<ProductWithPrice>();
}
public Order Order { get; set; }
public ICollection<ProductWithPrice> ProductWithPrices { get; set; }
}
You'll need to add #Html.HiddenFor(m => m.SellingPrice) and similarly for other bound fields that are using DisplayFor if you want to post them back to the controller.
Note: For your benefit, try to have a look at the generated HTML code when your page is rendered in the browser and see what tags are generated inside the <form> tag with a name attribute.
make sure you bind the appropriate property from the complex object, like the following:
#model BCMManci.ViewModels.OrderCreateGroup
...
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
...
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OrderCreateGroup.Order.Quantity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OrderCreateGroup.Order.Quantity, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Note:model.OrderCreateGroup.Order.Quantity would be one the your order's property.
hope this helps.
This question already has answers here:
MVC5 Razor html.dropdownlistfor set selected when value is in array
(3 answers)
Closed 5 years ago.
I was writing different type of code from Google but it is not display correct value in the dropdown. It shows the correct value in the textbox but not selected value in the dropdown.
Model
public Int64 EvaluationInfoID { get; set; }
public Int64 EvalCategoryID { get; set; }
public string EvalCategoryName { get; set; }
public string EvalCategoryDesc { get; set; }
public Int64 UserID { get; set; }
public DateTime? EvaluationDate { get; set; }
public int EvalTypeID { get; set; }
Controller
var models = new List<EvaluationInfoViewModel>();
ViewBag.GridState = this.GridRouteValues();
string serachCriteria = string.Format("created_by={0} and to_date(to_char(evaldate,'MM/DD/YYYY'),'MM/DD/YYYY')={1} ", Convert.ToInt32(Session["UserID"]), "to_date('" + evaldate.ToShortDateString() + "','MM/DD/YYYY')");
PODService.EvaluationInfo[] getData = service.GetEvaluationInfoBySearchCriteria(serachCriteria);
PODService.EvaluationCategory[] evalcat = service.GetEvalCategoryBySearchCriteria(" EC.Eval_ID= 1");//150811VP: For Self Evaluation Eval_ID=1
PODService.EvaluationType[] evalTypes = service.GetEvaluationTypeList();
ViewData["EVALTYPE"] = new SelectList(evalTypes, "EvaluationType_ID", "EvaluationType_Name");
for (var i = 0; i < getData.Length; i++)
{
models.Add(new EvaluationInfoViewModel());
}
for (var i = 0; i < models.Count; i++)
{
string serachCriteria1 = string.Format("created_by={0} and to_date(to_char(evaldate,'MM/DD/YYYY'),'MM/DD/YYYY')={1} ", Convert.ToInt32(Session["UserID"]), "to_date('" + evaldate.ToShortDateString() + "','MM/DD/YYYY')");
models[i].EvalCategoryID = getData[i].EvalCategory_ID;
models[i].EvalCategoryName = getData[i].EvalCategoryName;
//models[i].EvalCategoryDesc = getData[i].Eval_CategoryDescription;
models[i].UserID = Convert.ToInt64(Session["UserID"]);
models[i].EvaluationDate = getData[i].EvaluationDate;
models[i].EvalTypeID = getData[i].EvalTypeID;
ViewData["EVALTYPEInfo"] = new SelectList(evalTypes, "EvaluationType_ID", "EvaluationType_Name", models[i].EvalTypeID);
View
#model List<PODWeb.Models.EvaluationInfoViewModel>
#{
Layout = null;
}
#using (Html.BeginForm("EditEvaluationInfo", "EvaluationInfo", FormMethod.Post))
{
<table border="1px" style="margin-left: 122px">
<tr>
<td>
<table>
#for (var i = 0; i < Model.Count(); i++)
{
#Html.HiddenFor(m=>m[i].UserID)
#Html.HiddenFor(m=>m[i].EvaluationDate)
#Html.HiddenFor(m =>m[i].EvalTypeID)
<tr>
<td>
#Model[i].EvalCategoryName
#Html.EditorFor(m =>m[i].EvalTypeID)
</td>
<td>
#*#Html.DropDownListFor(m => m[i].EvalTypeID, ViewData["EVALTYPEInfo"] as IEnumerable<SelectListItem>, "Select Rating", new { #style = "width:200px" })*#
</td>
</tr>
<tr>
<td>
#Model[i].EvalCategoryDesc
#Html.HiddenFor(m => m[i].EvalCategoryID)
</td>
<td>
</td>
</tr>
<tr>
<td colspan="2">
<hr />
</td>
</tr>
}
<tr>
<td>
<input type="submit" name="Save" value="Save" class="t-button" style="margin-left: 471px" />
</td>
</tr>
</table>
</td>
</tr>
</table>
}
I want to display selected evaltypeid in dropdownlist but it is not displaying.
You have the code for the dropdown list commented out:
#*#Html.DropDownListFor(m => m[i].EvalTypeID,
ViewData["EVALTYPEInfo"] as IEnumerable<SelectListItem>,
"Select Rating", new { #style = "width:200px" })*#
You need to remove the outer #* and *#
#Html.DropDownListFor(m => m[i].EvalTypeID,
ViewData["EVALTYPEInfo"] as IEnumerable<SelectListItem>,
"Select Rating", new { #style = "width:200px" })
Also delete the #Html.EditorFor(m =>m[i].EvalTypeID) from your code.
I have an MVC 5 app where I am using a for loop so I can bind a collection when passing back to the controller. This works fine for all my properties except for the one that is based on a DropDownFor type.
The problem is the name of the property is not getting set to "product.[0].TypeOfSubscription.
I have tried 3 different ways: The first 2 method end up with a name of [0].TypeOfSubscription and the 3rd one does have the correct name product[0].TypeOfSubscription but there is no binding occuring when I pass it back to the controller.
I think the problem is that the 3rd option is binding but because it is hidden it is not getting the selected value assigned.
#Html.EnumDropDownListFor(modelItem => Model[i].TypeOfSubscription)
#Html.EnumDropDownListFor(modelItem => Model[i].TypeOfSubscription,
new { name = "product[" + #i + "].TypeOfSubscription"})
#Html.Hidden("product[" + #i + "].TypeOfSubscription",
Model[i].TypeOfSubscription)
Model
public class VmStoreProducts
{
public VmStoreProducts()
{
NoOfUsers = 1;
}
public enum SubscriptionType
{
Monthly,
Annual
}
public int MojitoProductId { get; set; }
[Display(Name = "Category")]
public string ProductCategory { get; set; }
public virtual string Name { get; set; }
public string Description { get; set; }
[Display(Name = "Image")]
public byte[] ImageData { get; set; }
[Display(Name = "Type of Subscription")]
public SubscriptionType TypeOfSubscription { get; set; }
public decimal Price { get; set; }
[Display(Name = "No. of Users")]
public int NoOfUsers { get; set; }
[Display(Name = "Total Price")]
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal TotalPrice { get; set; }
}
For Loop - View
#model PagedList.IPagedList<VmStoreProducts>
#using Mojito.Domain
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Mojito Products</h2>
<div class="col-md-9"></div>
<div class="col-md-3">
#using (Html.BeginForm("Index", "MojitoProducts", FormMethod.Get))
{
<p>
#Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
<input type="submit" value="Search" />
</p>
}
</div>
#using (Html.BeginForm("AddToCart", "ShoppingCart", FormMethod.Post))
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().ImageData)
</th>
<th>
#Html.ActionLink("Category", "Index", new { sortOrder = ViewBag.SortByCategory, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.ActionLink("Product", "Index", new { sortOrder = ViewBag.SortByProduct, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().Description)
</th>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().TypeOfSubscription)
</th>
<th>
#Html.ActionLink("Price", "Index", new { sortOrder = ViewBag.SortByPrice, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().NoOfUsers)
</th>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().TotalPrice)
</th>
<th></th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#if (Model[i].ImageData != null)
{
<div class="pull-left" style="margin-right: 10px">
<img class="img-thumbnail" width="75" height="75"
src="#Url.Action("GetImage", "MojitoProducts",
new { Model[i].MojitoProductId })" />
</div>
}
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].ProductCategory)
</td>
<td>
#Html.TextBox("product[" + #i + "].Name",
Model[i].Name, new { #readonly = "readonly" })
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].Description)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => Model[i].TypeOfSubscription)
#Html.EnumDropDownListFor(modelItem => Model[i].TypeOfSubscription,
new { name = "product[" + #i + "].TypeOfSubscription"})
#Html.TextBox("product[" + #i + "].TypeOfSubscription",
Model[i].TypeOfSubscription, new { hidden=true })
</td>
<td>
#Html.TextBox("product[" + #i + "].Price",
Model[i].Price, new { #readonly = "readonly", style = "width:50px" })
</td>
<td>
#Html.TextBox("product[" + #i + "].NoOfUsers",
Model[i].NoOfUsers, new { type = "number", min = "0", style = "width:50px" })
</td>
<td>
#Html.TextBox("product[" + #i + "].TotalPrice",
Model[i].TotalPrice, new { style = "width:50px" })
</td>
<td>
<div class="pull-right">
#if (Request.Url != null)
{
#Html.Hidden("product[" + #i + "].MojitoProductId",
Model[i].MojitoProductId)
#Html.Hidden("returnUrl", Request.Url.PathAndQuery)
}
</div>
</td>
</tr>
}
<tr>
<td colspan="6">
<div class="pull-right">
<input type="submit" class="btn btn-success" value="Add to cart" />
</div>
</td>
</tr>
</table>
}
Controller Method
public ActionResult AddToCart(List<VmStoreProducts> product, string returnUrl)
{
ShoppingCart cartObjects = (Session["CartObjects"] as ShoppingCart) ?? new ShoppingCart();
Session["CartObjects"] = cartObjects;
foreach (var item in product)
{
if (item.NoOfUsers > 0)
{
cartObjects.AddItem(item);
}
}
return RedirectToAction("Index", new { returnUrl });
}
Move the definition of the enum outside the VmStoreProducts class
public enum SubscriptionType
{
Monthly,
Annual
}
public class VmStoreProducts
{
public VmStoreProducts()
{
NoOfUsers = 1;
}
public int MojitoProductId { get; set; }
....
}
The for loop will name the selects
[0].TypeOfSubscription
[1].TypeOfSubscription
....
which will correctly bind on postback (assuming your action method is public ActionResult AddToCart(IEnumerable<VmStoreProducts> products) {...
Also, do not use
#Html.TextBox("product[" + #i + "].Name", Model[i].Name, new { #readonly = "readonly" })
Since you already using a DisplayFor for the same property a hidden input seems more appropriate, so
#Html.HiddenFor(m => m[i].Name)
or if you want to display it twice
#Html.TextBoxFor(m => m[i].Name, new { #readonly = "readonly" })
This applies to the other properties as well
Try using a textbox and hide it to persist the value or use another 'data- property
In case of DropDownListFor, when data is posted back to controller, selected value get lost, so we need to have a hidden textbox to keep the selected value