Not able to see ViewModel property in View? - c#

I'm still trying to get my head wrapped around using ViewModels and IEnumerable/List as properties. I'm obviously missing something that is not allowing me to see my IEnumerable in my View.
What I'm ultimately trying to do is have a view that will show a list of unassigned users not assigned to a Group (called Patrols in this case and would have PatrolId=0). The table will have a checkbox next to each member. Above that table will be a DropDownList of the available Patrols. The Admin will come to the page to see those that aren't assigned, select a Patrol from the DDL at the top, check the users he want's to assign to that Patrol, and then submit the form that will pass the PatrolID from the DDL and find all the selected members in order to update their record with that PatrolId.
In the View below when I go to loop through the Scout property, I would assume I would be able to use Model.Scout in this part in order to loop through and write out all the members in that IEnumerable.
#for (var i = 0; i < Model.Count(); i++)
However, when I try to use intellisense to see the Scout property when using the Model, I don't see the property. In addition to that I would think I would need to first check the Scout property to see if there is a count before I write out all the rows. Again, I can't see the Scout property in order to check it's count.
ViewModel
public class PatrolMemberViewModel
{
[Key]
public int MemberId { get; set; }
public int PatrolId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Phone")]
public string PhonePrimary { get; set; }
[Display(Name = "Email")]
public string EmailPrimary { get; set; }
public bool IsSelected { get; set; }
}
public class PatrolUnassignedViewModel
{
public SelectList Patrols { get; set; }
public IEnumerable<PatrolMemberViewModel> Scout { get; set; }
}
Controller
// GET:
public ViewResult Unassigned()
{
PatrolUnassignedViewModel unassinged = new PatrolUnassignedViewModel();
unassinged.Patrols = new SelectList(repository.SelectAllPatrols());
unassinged.Scout = repository.SelectAllUnassigned();
return View(unassinged);
}
Repository
public IEnumerable<PatrolMemberViewModel> SelectAllUnassigned()
{
using (DataContext db = new DataContext())
{
var results = (from p in db.Person
where p.IsActive == true
&& p.IsScout == true
&& p.PatrolId == 0
select new PatrolMemberViewModel
{
MemberId = p.PID,
FirstName = p.FirstName ?? string.Empty,
LastName = p.LastName ?? string.Empty,
EmailPrimary = p.EmailPrimary ?? string.Empty,
PhonePrimary = p.PhonePrimary ?? string.Empty,
PatrolId = p.PatrolId,
IsSelected = false
}
).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return results;
}
}
public IEnumerable<PatrolName> SelectAllPatrols()
{
return db.PatrolNames;
}
View
#model IList<ProjectName.ViewModels.PatrolUnassignedViewModel>
#{
ViewBag.Title = "Unassigned";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Patrols</h2>
<h4>Assign Scouts to a Patrol.</h4>
#using (Html.BeginForm("Update", "Patrol", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "alert alert-danger" })
<table class="table table-bordered table-striped table-hover table-condensed tbackground">
<tr>
<th class="text-center">
</th>
<th class="text-center">
First Name
</th>
<th class="text-center">
Last Name
</th>
<th class="text-center">
Email
</th>
<th class="text-center">
Phone
</th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m[i].LastName)
</td>
<td>
#Model[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m[i].PhonePrimary)
</td>
</tr>
}
</table>
<div class="control-wrapper">
<input type="submit" id="btnSubmit" value="Assign Scouts" class="btn btn-success" />
</div>
}
<p> </p>

You controller is returning a single instance of class PatrolUnassignedViewModel
public ViewResult Unassigned()
{
PatrolUnassignedViewModel unassinged = new PatrolUnassignedViewModel();
unassinged.Patrols = new SelectList(repository.SelectAllPatrols());
unassinged.Scout = repository.SelectAllUnassigned();
return View(unassinged);
}
Your view is expecting an IList
#model IList<ProjectName.ViewModels.PatrolUnassignedViewModel>
When it should be expecting
#model ProjectName.ViewModels.PatrolUnassignedViewModel
Your scout is an IEnumerable so doesn't have a count method so should be
public IList<PatrolMemberViewModel> Scout { get; set; }
public IList<PatrolMemberViewModel> SelectAllUnassigned()
{
}
You should be doing your loop like this
#for (var i = 0; i < Model.Scout.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m.Scout[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m.Scout[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m.Scout[i].LastName)
</td>
<td>
#Model.Scout[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m.Scout[i].PhonePrimary)
</td>
</tr>
}
Am I missing something here?

From your GET action method, you are passing a single object of PatrolUnassignedViewModel. But your view is bound to a collection of PatrolUnassignedViewModel. So change your view to be like
#model PatrolUnassignedViewModel
Now you can use the Model.Scout property which is a collection.
#model PatrolUnassignedViewModel
<h2>Total : #Model.Scout.Count()</h2>
#foreach(var item in Model.Scout)
{
<label>#item.FirstName</label>
}

Related

Can't pass an enumerable model to a controller?

I'm a bit confused because I thought this a very straight-forward thing, it's possibly something simple tripping me up.
I have a view:
#model IEnumerable<CarViewModel>
#using (Html.BeginForm("SummarySaveAll", "VroomVroom", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
#Html.DisplayNameFor(model => model.Driver)
</th>
<th width="1">
#Html.DisplayNameFor(model => model.Colour.Name)
</th>
</tr>
</thead>
<tbody>
#foreach (var element in Model)
{
<tr>
<td width="1">
#Html.DisplayFor(m => element.Driver)
</td>
<td width="1">
#Html.DropDownListFor(m => element.Colour, element.Colours, "Unknown")
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
and the list/enumerable of CarViewModel is supposed to bounce back to the VroomVroom controller, action SummarySaveAll which it does - but the viewmodel on the page doesn't get passed back to it:
[HttpPost]
public ActionResult SummarySaveAll(IEnumerable<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View();
}
I tried to encapsulate the List in another ViewModel and cycle through elements using a for i loop but that wouldn't pass back to the controller either.
Surely it's possible to send a List or IEnumerable of models back to a controller?
My CarVM:
public class CarViewModel
{
[MaxLength(150)]
[Display(AutoGenerateField = true, Name = "Entered By")]
public string Driver { get; set; }
[Display(AutoGenerateField = true)]
public Colour Colour { get; set; }
[Key]
[Display(AutoGenerateField = false)]
public int Id { get; set; }
[Display(AutoGenerateField = false)]
public bool IsDeleted { get; set; } = false;
public IEnumerable<SelectListItem> Colours { get; set; }
public CarViewModel() { }
public CarViewModel(Model CarModel summaryModel, CarPropertyCollection propertyCollection)
{
Driver = summaryModel.Driver;
Id = summaryModel.Id;
IsDeleted = summaryModel.IsDeleted;
Colour = summaryModel.Colour == null ? null :
propertyCollection.Colours.Where(x => x.Id == summaryModel.Colour.Id).FirstOrDefault();
Colours = propertyCollection.Colours.Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name });
}
}
}
Must stress that Colour is a custom class but only has Id and Name properties
Colours doesn't relate to a specific car, it relates to cars in general, so rather than using a collection as your view model, create a wrapper:
class EditCarsViewModel
{
public IEnumerable<SelectListItem> Colours { get; set; }
public IList<CarViewModel> Cars { get; set; }
}
Then your view:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour, Model.Colours, "Unknown")
</td>
}
Any other CarViewModel properties will need their own input as well. HiddenFor can be used if they should be readonly:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
#Html.HiddenFor(m => Model.Cars[i].Id)
#Html.HiddenFor(m => Model.Cars[i].Driver)
<!-- etc. -->
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour.Id, Model.Colours, "Unknown")
</td>
}
And your controller:
[HttpPost]
public ActionResult SummarySaveAll(EditCarViewModel model)
{
// model.Cars should be populated
return View();
}
Note that an indexable collection, such as IList<T> should be used, as the form field names need to include the index to differentiate the items.
Edit by OP
The Colour class consists of a [Key] int Id property and a string Name property. For DropDownList items I had to make sure the Id property was specified on the m => Model.Cars[i].Colour.Id line otherwise that particular prop was coming back as null even though other items were coming through fine.
try
[HttpPost]
public ActionResult SummarySaveAll(IList<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View(summaries);
}
I've also added this model as a param for your view
This how you do it:
First my View which posts back to a controller named Home and an action named ListView:
#model List<MyModel>
#{
ViewData["Title"] = "Using a list as model";
}
<h1>#ViewData["Title"]</h1>
#using (Html.BeginForm("ListView", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
Name
</th>
<th width="1">
Description
</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td width="1">
#Html.DisplayFor(m => Model[i].Name)
</td>
<td width="1">
#Html.TextBoxFor(m => Model[i].Description)
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
Notice how I used an indexer to render the controls [i]
This is my model:
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
}
This is my controller action:
[HttpPost]
public IActionResult ListView(IEnumerable<MyModel> model)
{
return View(model);
}
And this is the result:

How can I pass multiple values of a table/array to my controller

I create a table in my CSHTML, I want to pass the array of nr of items == aantal back to my controller however this doesn't seem to work. Any idea whats wrong or why I get a null reference in my controller?
CSHTML
#using (Html.BeginForm("OrderConfirm", "Beurs", new { vm = Model.Aantal }, method: FormMethod.Post))
{
<table class="table table-striped table-condensed table-bordered">
<tr>
<th>
Naam
</th>
<th>
Prijs
</th>
<th>
Minimum prijs
</th>
<th>
Factor
</th>
<th> Actie</th>
<!--
<th>Edit</th>-->
</tr>
#foreach (var item in Model.ItemLijstVm)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Naam)
</td>
<td>
€ #Html.DisplayFor(modelItem => item.Prijs)
</td>
<td>
€ #Html.DisplayFor(modelItem => item.MinimumPrijs)
</td>
<td>
#Html.DisplayFor(modelItem => item.Factor)
</td>
<td>
#Html.TextBoxFor(m => m.Aantal[item.Id - 1], new {type = "number" })
</td>
</tr>
}
</table>
<input type="submit" value=" Bevestig bestelling " width="120" />
}
ViewModel
public class BeursLijstViewModel
{
public IEnumerable<BeursItemViewModel> ItemLijstVm{get; set;}
public string Naam { get; set; }
public double Crash { get; set; }
//References naar animated gif
public bool Event { get; set; }
public string GifPath { get; set; }
public int[] Aantal { get; set; }
public int VerhoogAllePrijzen { get; set; }
public double Totaal { get; set; }
public SelectListItem Categorie { get; set; }
public BeursLijstViewModel(Beurs beurs)
{
ItemLijstVm= beurs.Items.Select(g => new BeursItemViewModel(g));
Naam = beurs.Naam;
Aantal = new int[beurs.Items.Count()];
Totaal = beurs.Totaal;
}
}
Controller
[HttpPost]
public ActionResult OrderConfirm(int[] vm) //VM is null but should be array
{
//Some more code
}
The reference I get on my post from my model is null, but if i declare it in my foreach loop like this, it works. I really don't have a clue what goes wrong:
#using (Html.BeginForm("Add", "Beurs", new { id = item.Id, aantal = Model.Aantal }, method: FormMethod.Post))
{
#Html.TextBoxFor(m => m.Aantal[item.Id - 1], new { type = "number" })
<input type="submit" value=" + " width="120"/>
}
I think you have to pass back the actual model not just array expecting to get result, since when it binds it goes several layers like name='Model.item[z]'
[HttpPost]
public ActionResult OrderConfirm(BeursLijstViewModel vm)
{
foreach (var item in vm.ItemLijstVm)
{
var response= vm.Aantal[item.Id - 1]
}
}
#using (Html.BeginForm("OrderConfirm", "Beurs", new { vm = Model.Aantal }, method: FormMethod.Post))
changing the above part in combination with the answer of #COLD TOLD to
#using (Html.BeginForm("OrderConfirm", "Beurs"))
fixed my problem. Thanks for the help!

Pass ID from view of type IEnumerable using HTML Actionlink in Razor

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!

Trying to get the Selected checkbox values with the ID value

Below is the Model
public class M_ProjectType
{
public Int16 ProjectTypeID { get; set; }
public String ProjectType { get; set; }
public Boolean IsActive { get; set; }
public Decimal Cost { get; set; }
public String Description { get; set; }
public Boolean IsChecked { get; set; }
}
Below is View Model
public class VM_Project
{
public string[] SkillID { get; set; }
public List<M_ProjectType> ProjectType { get; set; }
}
Below is Get Action method. here I am getting the data for projects that will be sent to View Model
[HttpGet, Route("Project")]
public async Task<ActionResult> Project()
{
var projectTypes = (await _projectTypes.ProjectTypesList()).Value;
var list = new List<M_ProjectType>();
foreach (var item in projectTypes)
{
list.Add(new M_ProjectType
{
Cost = item.Cost,
Description = item.Description,
IsActive = item.IsActive,
IsChecked = false,
ProjectType = item.ProjectType,
ProjectTypeID = item.ProjectTypeID
}
);
}
var project = new VM_Project
{
ProjectType = list
};
return View(project);
}
Below is Razor View
#foreach (var item in Model.ProjectType)
{
<table class="table table-striped">
<tbody>
<input type="hidden" value="#item.ProjectTypeID" name="ProjectTypeID" />
<tr>
<td style="width:5%">
#Html.CheckBoxFor(i => item.IsChecked, new { #class = "tableflat" })
#Html.HiddenFor(i => item.ProjectTypeID)
</td>
<td style="width:10%">#item.ProjectType</td>
<td style="width:80%">#item.Description</td>
<td style="width:5%"><b>$#item.Cost</b></td>
</tr>
</tbody>
</table>
}
Below is Post Action Method
[HttpPost, Route("Project")]
public ActionResult Project(VM_Project project)
{
return View();
}
Question: I am getting project.ProjectType = null. Any suggestion why
this is happening ?
I would recommend using EditorTemplates.
Create a folder named EditorTemplates in you Views/Shared direcotry.
Create a partial view based on your type i.e. M_ProjectType.cshtml
Put your markup that you use in foreach loop in M_ProjectType.cshtml file
#model M_ProjectType
<table class="table table-striped">
<tbody>
<tr>
<td style="width:5%">
#Html.CheckBoxFor(i => i.IsChecked, new { #class = "tableflat" })
#Html.HiddenFor(i => i.ProjectTypeID)
</td>
<td style="width:10%">#Model.ProjectType
#Html.HiddenFor(i=>i.ProjectType)
</td>
<td style="width:80%">#Model.Description</td>
<td style="width:5%"><b>$#Model.Cost</b></td>
</tr>
</tbody>
Then render your editor template in your form like (note: no foreach loop)
#Html.EditorFor(m=>m.ProjectType)
You should get correct model binded to your html elements back in controller.
Try this:
#foreach (var item in Model.ProjectType)
{
<table class="table table-striped">
<tbody>
<tr>
<td style="width:5%">
#Html.CheckBoxFor(i => item.IsChecked, new { #class = "tableflat" })
#Html.HiddenFor(i => item.ProjectTypeID, new { #Value = item.ProjectTypeID})
</td>
</tr>
</tbody>
</table>
}

MVC Enum Model Binding in For Loop

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

Categories

Resources