Sessions in MVC 5 asp.net - asp.net-mvc

I am doing a project about an Chinese food ordering system, and I am wanting show selected items in a view on another page. I am trying to use sessions, but I keep getting different errors, like "The model item passed into the dictionary is of type" and "object reference is not set to an instance of an object". How can I select the data, and show them in a different view? The project is linked up to a database, the DishID and other instances should be connected through the database.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using KinaRest.Models;
using KinaRest.View_Models;
using KinaRest.Infrastructure;
namespace KinaRest.Controllers
{
public class TakeawayController : Controller
{
private Basket basket = new Basket();
[ChildActionOnly]
public ActionResult BasketItemsList()
{
Basket basket = new Basket();
return View("_Basket", basket.Items);
}
// GET: /Takeaway/
private ChinaContext db = new ChinaContext();
[HttpGet]
public ActionResult Index()
{
return View(db.Dish.ToList());
}
[HttpPost]
public ActionResult Index(BasketItem basketItem)
{
//Repository repository = new Repository();
if (Session["Basket"] == null)
{
Session["Basket"] = new Basket();
}
else
{
basket = (Basket)Session["Basket"];
}
basket.AddItem(basketItem);
return View("Test");
}
}
}
Model 1: Basket
namespace KinaRest.Infrastructure
{
public class Basket
{
private List<BasketItem> items = new List<BasketItem>();
public List<BasketItem> Items { get { return items; } }
public void AddItem(BasketItem bi) {
items.Add(bi);
}
}
}
Model 2: BasketItem
namespace KinaRest.Infrastructure
{
public class BasketItem
{
public int DishId {get; set; }
public int Number { get; set; }
public decimal Price { get; set; }
}
}
In this view, we are trying to collect the data, such as the ID, the quantity(number) and price with a form:
#model IEnumerable<KinaRest.Models.Dish>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div class="form-group">
#using (Html.BeginForm())
{
foreach (var item in Model)
{
<div class="col-md-10">
#Html.DisplayFor(modelItem => item.DishId)
</div>
<div class="col-md-10">
#Html.DisplayFor(modelItem => item.Title)
</div>
<div class="col-md-10">
#Html.DisplayFor(modelItem => item.Price)
</div>
<div class="col-md-10">
#Html.DisplayFor(modelItem => item.Description)
</div>
<div class="col-md-10">
#Html.DisplayFor(modelItem => item.Type.Type1)
</div>
<img src="#Url.Content("~/content/images/" + item.Image)" />
#Html.EditorFor(model => item.Number, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => item.Number, "", new { #class = "text-danger" })
<input type="submit" value="Tilføj" class="btn btn-default" />
}
}
</div>
In this view, we are trying to access the sessions data:
#model List<KinaRest.Infrastructure.BasketItem>
#{
ViewBag.Title = "Test";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Test</h2>
<ul>
#foreach (var item in Model)
{
<li>
#Html.DisplayFor(modelItem => item.Number)
</li>
}
</ul>
and yes, I am new to MVC and I am still learning. Hope you guys can help.

[HttpGet]
public ActionResult Order(int id)
{
Dish dish = db.Dish.Single(d=> d.DishId == id); //database call here
BasketItem basketItem = new BasketItem(){DishId = dish.?, Number = dish.?, Price = dish.Price }; // create a new BasketItem
Basket basket; //create reference outside if block
//Repository repository = new Repository();
if (Session["Basket"] == null)
{
basket = new Basket();
Session["Basket"] = basket;
}
else
{
basket = (Basket)Session["Basket"];
}
basket.AddItem(basketItem);
return View("Test", basket.Items); //or return View(((Basket)Session["Basket"]).Items); // give the view the data
}
take out the class field basket. As far as i know the controller is instanciated every time when a request is to be processed
View for ordering
#model IEnumerable<KinaRest.Models.Dish>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<table>
<tr><th></th><th>Dish</th><th></th></tr>
#foreach(var item in Model)
{
<tr><td><img src="#Url.Content("~/content/images/" + item.Image)" /></td><td>#html.displayfor(item => item.Name)</td><td>#Html.ActionLink("Add", "Order", "TakeAway", new {id = item.DishId})</td></tr>
}
</table>

Related

How to pass different list of data from view to controller MVC

currently I facing a very tricky problem of passing different list of data from view to controller.
I have created two input box to submit my data to controller so that it can be saved into CreateAccountsDB and further display it in the list of
Selected Subcon when Create button is pressed.
The problem I face here is:
when pressing the Create button with entered data from NewCompanyName textbox and NewEmail textbox, those entered data do pass data from View to Controller and save data into CreateAccountDB (not showing in View), but the entered data is not displaying in the list of Selected Subcon.
Create View
Here is the model.
public class Tender
{
public int ID { get; set; }
public string CompanyName { get; set; }
public List<CreateAccount> FrequentCompanyName { get; set; }
public List<CreateAccount> SuggestCompanyName { get; set; }
public List<CreateAccount> SelectedCompanyName { get; set; }
public string CompanyNameNew { get; set; }
public string EmailNew { get; set; }
public int? TradeID { get; set; }
public virtual Trade Trade { get; set; }
public int? CreateAccountID { get; set; }
public virtual CreateAccount CreateAccount { get; set; }
}
Here is the Get Method of Create function in controller:
[httpGet]
public ActionResult Create(int? id)
{
Tender tender = new Tender();
tender.FrequentCompanyName = db.createaccountDB.Include(tm => tm.Trade).Where(td => td.Frequency == 32).ToList();
tender.SuggestCompanyName = db.createaccountDB.Include(tm => tm.Trade).ToList();
if (tender.SelectedCompanyName == null)
{
tender.SelectedCompanyName = new List<CreateAccount>().ToList();
}
return View(tender);
}
and Here is my Post Method of Create function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,CompanyName,TradeID,FrequentCompanyName,SelectedCompanyName,CreateAccountID")] Tender tender ,string CompanyNameNew, string Emailnew)
{
CreateAccount accnew = new CreateAccount();
accnew.CompanyName = CompanyNameNew;
accnew.Email = Emailnew;
if(ModelState.IsValid)
{
db.createaccountDB.Add(accnew);
db.SaveChanges();
}
if (tender.SelectedCompanyName == null)
{
tender.SelectedCompanyName = new List<CreateAccount>().ToList();
}
tender.FrequentCompanyName = db.createaccountDB.Include(tm => tm.Trade).Where(td => td.Frequency == 32).ToList();
tender.SuggestCompanyName = db.createaccountDB.Include(tm => tm.Trade).ToList();
tender.SelectedCompanyName.ToList().Add(accnew);
return View(tender);
}
and Here is my Create View:
#model Tandelion0.Models.Tender
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-group">
#*#Html.LabelFor(model => model.ProjectName, htmlAttributes: new { #class = "control-label col-md-3" })*#
<div class="col-md-3">
<h5>New Company Name</h5>
#Html.EditorFor(model => model.CompanyNameNew, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CompanyNameNew, "", new { #class = "text-danger" })
</div>
<div class="col-md-3">
<h5>New Email</h5>
#Html.EditorFor(model => model.EmailNew, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EmailNew, "", new { #class = "text-danger" })
</div>
<div class="container" align="center">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
<div class="container row">
<!--selected subcon column-->
<div class="container row col-sm-4">
<h4>
Selected Subcon
</h4>
<div style="overflow-y: scroll; height:250px;">
<table class="table table-hover">
#foreach (var item in Model.SelectedCompanyName)
{
<tbody>
<tr>
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
</tr>
</tbody>
}
</table>
</div>
</div>
</div>
}
So far I manage to save data from view into CreateAccountsDB when create button is pressed, but those data just couldn't pass it from Post method Create function to Get method Create function in Controller. The data and the list become null immediate after come out from post method Create function.
Because of data becomes null, the view couldn't receive any data from controller.
May I know how can i solve the the problem of passing data from controller to view? Is the way I pass data totally wrong?
Any advice is truly appreciated.
In your HttpPost Action method :
Instead of :
tender.SelectedCompanyName.ToList().Add(accnew);
You should be doing:
tender.SelectedCompanyName.Add(accnew);
Calling ToList().Add(object) won't actually add to SelectedCompanyName.Instead it will add to the new list object created by calling ToList() method which you are not assigning back to tender.SelectedCompanyName.
A better approach however would be to use Post/Redirect/Get Pattern.
Instead of returning a view from your post method , do a temorary redirect to your [HttpGet]Create action method passing the id of the tender.

Add to list mvc

I'm trying to add string data to list, but have null reference exception and don't have any idea how to fix this.
Here home controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
**model.chatGroup.Add(model.inputGroups);** - here excepton
}
return View(model);
}
And Index.cshtml:
#model test.Models.ChatUser
#{
ViewBag.Title = "Test";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="form-group">
#Html.LabelFor(m => m.inputGroups, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.inputGroups, new { #class = "form-control" })
<input type="submit" value="GO" class="btn btn-default" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Your form does not include any controls for properties of chatGroup so its null when you post back. You need to either initialize the property in a parameterless constructor
public class ChatUser
{
public ChatUser()
{
chatGroup = new List<string>(); // initialize collection
}
....
public List<string> chatGroup { get; set; }
}
or initialize it in the POST method
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
model.chatGroup = new List<string>(); // initialize collection
model.chatGroup.Add(model.inputGroups);
}
return View(model);
}

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'CategoryID

I have found a lot of similar threads on this problem. But whatever I do, it never works for me.
The ONLY thing I want to achieve right now, is to fill a dropdown list with database values in a partial view, that's within a partial view. That is all, and this is driving me absolutely nuts. A user should be able to chose a category name in the dropdown list.
Here's my Controller Class:
public class AttributeController : Controller
{
private readonly IRepository<ClassLibrary.Entities.Attribute> _aRepository;
private readonly IRepository<Category> _cRepository;
public AttributeController() : this(new Repository<ClassLibrary.Entities.Attribute>(), new Repository<Category>())
{
}
public AttributeController(IRepository<ClassLibrary.Entities.Attribute> repo, IRepository<Category> repository)
{
_aRepository = repo;
_cRepository = repository;
}
//
// GET: /Attribute/
public ActionResult Index()
{
var attributes = _aRepository.GetAll();
var attributeViewModels = new List<AttributeViewModel>();
foreach (ClassLibrary.Entities.Attribute attribute in attributes)
{
var viewModel = new AttributeViewModel();
viewModel.Id = attribute.Id;
viewModel.AttributeName = attribute.Name;
attributeViewModels.Add(viewModel);
}
return View(attributeViewModels);
}
//
// GET: /Attribute/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Attribute/Create
public ActionResult Create()
{
ViewData["CategoryID"] = new SelectList(_cRepository.GetAll().ToList(), "Category_id", "Category");
IEnumerable<SelectListItem> items = _cRepository.GetAll().Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
ViewData["CategoryID"] = items;
return View();
}
//
// POST: /Attribute/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And here's the index View:
#model IEnumerable<GUI.Models.AttributeViewModel>
#{
ViewBag.Title = "Attributes";
}
<div>
#Html.Partial("~/Views/Attribute/Create.cshtml", new GUI.Models.AttributeViewModel())
</div>
<h2>All existing Attributes</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.AttributeName)
</th>
<th></th>
</tr>
#if(Model != null)
{
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.AttributeName)
</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>
Here's the partial View Within the index view:
#model GUI.Models.AttributeViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create an attribute for this Category</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.Partial("~/Views/Attribute/CategoryPartial.cshtml", new GUI.Models.CategoryViewModel())
<div class="form-group">
#Html.LabelFor(model => model.AttributeName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AttributeName)
#Html.ValidationMessageFor(model => model.AttributeName)
</div>
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And finally CategoryPartialView within the partial view that will contain the DropDown List where the user should be able to select a category Name.
#model GUI.Models.CategoryViewModel
#using GUI.Controllers
#using System.Collections.Generic
#{
//var categories = (IEnumerable<GUI.Models.CategoryViewModel>)ViewData["categories"];
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>)ViewData["CategoryID"])
</div>
</div>
}
The exception ALWAYS occurs when I'm debugging the application and it points to the #Html.DropDownList part. Claiming that "There is no ViewData item of type 'IEnumerable' that has the key 'CategoryID".
#Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>)ViewData["CategoryID"]
I'm sorry for posting so many code blocks. But I would really like to solve this.I've beaten my head against the wall for hours with frustration, and any help would be greatly appreciated.
When you use #Html.Partial("~/Views/Attribute/CategoryPartial.cshtml", new GUI.Models.CategoryViewModel()) you are directly instantiating the partial view. The controller does not get called and is not responsible for generating the HTML markup string. Additionally, you are passing an empty view model (new GUI.Models.CategoryViewModel()). Since the controller does not get called, it does not get the data for the drop down list and, obviously, does not save it in your ViewData.
Use the #Html.Action helper instead:
<div class="form-group">
#Html.Action("Create", "Attribute")
<div class="form-group">
Change the Create action like this:
//
// GET: /Attribute/Create
public ActionResult Create()
{
ViewData["CategoryID"] = new SelectList(_cRepository.GetAll().ToList(), "Category_id", "Category");
IEnumerable<SelectListItem> items = _cRepository.GetAll().Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
ViewData["CategoryID"] = items;
return PartialView("CategoryPartial", new GUI.Models.CategoryViewModel());
}
Note: I don't know your entire structure, but I might be best to use this approach and to return a Lis<SelectListItem> model.
when calling:
#Html.Partial("~/Views/Attribute/Create.cshtml", new GUI.Models.AttributeViewModel())
you call the view directly with out passing throw the controller so the model you have there is an empty model!
try pass throw the controller to create you'r model and send it back as a view:
#Html.RenderPartial("YOUR CONTROLLER",model);

How to create view for given model

I am new to asp .net mvc 4.0. i have given model. i am not getting how can i create view for model. I am facing problem at IList JournalEntries. other entry i am able to do.
public class Journal : BaseClass
{
public virtual string VoucherNo { get; set; }
public virtual DateTime VoucherDate { get; set; }
public string VoucherDateView {
get
{
return VoucherDate.ToShortDateString();
}
}
public IList<JournalEntry> JournalEntries { get; set; }
public IList<Ledger> Accounts { get; set; }
public double TotalAmount
{
get
{
double sum = 0;
if (JournalEntries != null && JournalEntries.Count>0)
foreach (var journal in JournalEntries)
sum = journal.Principal + journal.Interest+sum;
return sum;
}
}
}
I have tried below view but add entry doesn't works.
#model Sms.CoreSociety.Journal
#{
ViewBag.Title = "Create";
}
#{
string data = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model);
}
<script type="text/javascript">
$(document).ready(function () {
$('#document').validate();
$("#VoucherDate").mask("99/99/9999", { placeholder: " " });
function entryVm(entries) {
var self = this;
self.entryList = ko.observableArray(entries);
self.entry = ko.observable();
self.rowClick = function(entry1) {
alert("Delete alert");
self.dispatchList.remove(entry1);
};
self.addEntry = function() {
alert("Add alert");
this.entryList.push({ AccountName_AccountHead: "", DebitCredit: "", Principal: "0.0", Interest: "0.0", Narration: ""});
};
}
var models = #Html.Raw(Json.Encode(Model.JournalEntries)) ;
ko.applyBindings(new entryVm(models));
});
</script>
#using (Html.BeginForm(null, null, FormMethod.Post, new Dictionary<string, object>() { { "class", "form-horizontal" }, { "id", "document" } }))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="row">
<div class="span1">
<label>Voucher No</label>
</div>
<div class="span5">
#Html.DisplayFor(model => model.VoucherNo)
</div>
</div>
<div class="row">
<div class="span1">
<label>Voucher Date</label>
</div>
<div class="span5">
#Html.TextBoxFor(model => model.VoucherDate, "{0:dd/MM/yyyy}", new Dictionary<string, object>() { { "class", "required" } })
</div>
</div>
<div class="row">
<div class="span1">
<label>Amount</label>
</div>
<div class="span5">
#Html.DisplayFor(model => model.TotalAmount)
</div>
</div>
<input type="submit" value="Save" class="btn" id="submit"/>
#if (Model.Id != new Guid())
{
<div style="float: right">
<a class="btn btn-danger" href='#Url.Action("Delete")/#Model.Id' aria-hidden="true">Delete</a>
</div>
}
</fieldset>
}
<h4>Journal Entry</h4>
<p >Entry for<span data-bind="text: entryList().length"> </span> entry(s)</p>
<button data-bind="click: addEntry" class="btn">Add Record</button>
<table>
<tbody data-bind="template: { name: 'entryRowTemplate', foreach: entryList }"></tbody>
</table>
<script type="text/html" id="entryRowTemplate">
<tr>
<td>AccountName_AccountHead: \$ <input data-bind="value: AccountName.AccountHead"/> </td>
<td>DebitCredit: \$ <input data-bind="value: DebitCredit"/></td>
<td>Principal: \$ <input data-bind="value: Principal"/></td>
<td>Interest: \$ <input data-bind="value: Interest"/></td>
<td>Narration: \$ <input data-bind="value: Narration"/></td>
<td>Delete</td>
</tr>
</script>
below is my Journal controller
using System;
using System.Linq;
using System.Web.Mvc;
using Sms.CoreSociety;
using System.Collections.Generic;
namespace SmsModernUI.Controllers
{
public class JournalController : BaseController
{
//
// GET: /AccountGroup/
public ActionResult Index()
{
var journals = Repository.GetAll<Journal>().OrderBy(x => x.VoucherNo);
return View(journals);
}
public ActionResult Create(Guid id)
{
if (id == new Guid())
{
var journal = new Journal();
string lastvoucherno = Repository.GetAll<Journal>().OrderBy(x => x.VoucherNo).Last().VoucherNo;
journal.VoucherNo = (int.Parse(lastvoucherno) + 1).ToString();
journal.VoucherDate = System.DateTime.Now;
journal.JournalEntries = new List<JournalEntry>();
journal.Accounts = Repository.GetAll<Ledger>();
return PartialView(journal);
}
var journal1 = Repository.Get<Journal>(id);
journal1.JournalEntries = Repository.GetAll<JournalEntry>(x => x.Journal.Id == id);
journal1.Accounts = Repository.GetAll<Ledger>();
return PartialView(journal1);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(Journal journal)
{
if (journal.Id == new Guid())
{
var jj = Repository.Save(journal);
foreach (var journalentry in journal.JournalEntries)
{
journalentry.Id = jj.Id;
Repository.Save(journalentry);
}
}
else
{
Journal jr = Repository.Get<Journal>(journal.Id);
var entries = Repository.GetAll<JournalEntry>(x=>x.Journal.Id == journal.Id);
foreach (var entry in entries)
{
Repository.Delete(entry);
}
var jj = Repository.Save(journal);
foreach (var journalentry in journal.JournalEntries)
{
journalentry.Id = jj.Id;
Repository.Save(journalentry);
}
}
return RedirectToAction("Index");
}
public ActionResult Index1()
{
Journal journal1 = Repository.Get<Journal>(new Guid("7A6EEBBC-2F3A-4A27-ACF8-A1D40115A68F"));
journal1.JournalEntries = Repository.GetAll<JournalEntry>(x => x.Journal.Id == journal1.Id);
journal1.Accounts = Repository.GetAll<Ledger>();
return View(journal1);
}
public ActionResult Delete(Guid id)
{
Journal jr = Repository.Get<Journal>(id);
var entries = Repository.GetAll<JournalEntry>(x => x.Journal.Id == jr.Id);
foreach (var entry in entries)
{
Repository.Delete(entry);
}
var result = Repository.Delete(jr);
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Create1(Journal journal)
{
var temp = journal;
return RedirectToAction("Create",journal.Id);
}
}
}
Views are not genereted from models. You need Controller Action method to pass your model to View.
public ActionResult()
{
var model = new Journal
{
//**define here value of model's properties, that you need in View
}
return View(model);
}
EDITED: After your addition.
I would devide it into two parts. Create ViewModel and pass it from View To Controller.
public JurnalViewModel
{
public Journal journal {get; set;}
public IList<JournalEntry> JournalEntries {get; set;}
}
Than in Create action first create journal and after foreach JournalEntries in model create new JournalEntry.
EDITED 2 To your comment. Quick sample:
[HttpPost]
public ActionResult Create (JurnalViewModel model)
{
var journal = new Journal();
db.Journals.Add(journal);
journal.name = model.journal.name
.....
//**some code
db.SaveChanges()
foreach(var item in model.JournalEntries )
{
var entry = new JournalEntry()
db.JournalEntries .Add(entry);
entry.property = item.property;
....
//**some code
db.SaveChanges()
}
}
Your problem is that you have no class constructor for JournalEntries.
public Journal()
{
JournalEntries = new List<JournalEntry>();
Accounts = new List<Ledger>();
}
Right click to your Action method inside controller and click add view then check create strongly typed-view checkbox then choose your desired model from dropdown in displayed dialogue box

View Model IEnumerable<> property is coming back null (not binding) from post method?

I have a view model that contains a Product class type and an IEnumerable< Product > type. On the post the first level product object comes back binded from the viewmodel whereas the product enum is coming back null.
Why is the IEnumerable< Prouduct> property not getting binded to my view model per the post? Thx!
Models:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductIndexViewModel
{
public Product NewProduct { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class BoringStoreContext
{
public BoringStoreContext()
{
Products = new List<Product>();
Products.Add(new Product() { ID = 1, Name = "Sure", Price = (decimal)(1.10) });
Products.Add(new Product() { ID = 2, Name = "Sure2", Price = (decimal)(2.10) });
}
public List<Product> Products {get; set;}
}
View:
#model ProductIndexViewModel
#using (#Html.BeginForm())
{
<div>
#Html.LabelFor(model => model.NewProduct.Name)
#Html.EditorFor(model => model.NewProduct.Name)
</div>
<div>
#Html.LabelFor(model => model.NewProduct.Price)
#Html.EditorFor(model => model.NewProduct.Price)
</div>
<div>
<input type="submit" value="Add Product" />
</div>
foreach (var item in Model.Products)
{
<div>
#Html.LabelFor(model => item.ID)
#Html.EditorFor(model => item.ID)
</div>
<div>
#Html.LabelFor(model => item.Name)
#Html.EditorFor(model => item.Name)
</div>
<div>
#Html.LabelFor(model => item.Price)
#Html.EditorFor(model => item.Price)
</div>
}
}
Controller:
public class HomeController : Controller
{
BoringStoreContext db = new BoringStoreContext();
public ActionResult Index()
{
ProductIndexViewModel viewModel = new ProductIndexViewModel
{
NewProduct = new Product(),
Products = db.Products
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(ProductIndexViewModel viewModel)
{
// ???? viewModel.Products is NULL here
// ???? viewModel.NewProduct comes back fine
return View();
}
You are not using your lambda expression properly. You need to be accessing the Products list through the model. Try doing it like this:
#count = 0
foreach (var item in Model.Products)
{
<div>
#Html.LabelFor(model => model.Products[count].ID)
#Html.EditorFor(model => model.Products[count].ID)
</div>
<div>
#Html.LabelFor(model => model.Products[count].Name)
#Html.EditorFor(model => model.Products[count].Name)
</div>
<div>
#Html.LabelFor(model => model.Products[count].Price)
#Html.EditorFor(model => model.Products[count].Price)
</div>
#count++
}
Edit
Controller:
BoringStoreContext db = new BoringStoreContext();
public ActionResult Index()
{
ProductIndexViewModel viewModel = new ProductIndexViewModel
{
NewProduct = new Product(),
Products = db.Products
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(ProductIndexViewModel viewModel)
{
// work with view model
return View();
}
Model
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductIndexViewModel
{
public Product NewProduct { get; set; }
public List<Product> Products { get; set; }
}
public class BoringStoreContext
{
public BoringStoreContext()
{
Products = new List<Product>();
Products.Add(new Product() { ID = 1, Name = "Sure", Price = (decimal)(1.10) });
Products.Add(new Product() { ID = 2, Name = "Sure2", Price = (decimal)(2.10) });
}
public List<Product> Products { get; set; }
}
View:
#model Models.ProductIndexViewModel
#using (#Html.BeginForm())
{
<div>
#Html.LabelFor(model => model.NewProduct.Name)
#Html.EditorFor(model => model.NewProduct.Name)
</div>
<div>
#Html.LabelFor(model => model.NewProduct.Price)
#Html.EditorFor(model => model.NewProduct.Price)
</div>
for (int count = 0; count < Model.Products.Count; count++ )
{
<div>
#Html.LabelFor(model => model.Products[count].ID)
#Html.EditorFor(model => model.Products[count].ID)
</div>
<div>
#Html.LabelFor(model => model.Products[count].Name)
#Html.EditorFor(model => model.Products[count].Name)
</div>
<div>
#Html.LabelFor(model => model.Products[count].Price)
#Html.EditorFor(model => model.Products[count].Price)
</div>
}
<div>
<input type="submit" value="Add Product" />
</div>
}
Travis's answer will address the issue. Here is another approach using EditorTemplates
You can use an Editor template to display the Products and it will work fine.
1) Create a Folder called "EditorTemplates" under your Views/Home folder
2 ) Add a view called "Products" inside that.
Add this code to that view
#model SO_MVC.Models.Product
#{
Layout = null;
}
<p>
#Html.LabelFor(x=>x.ID)
#Html.EditorFor(x=>x.ID)
</p>
<p>
#Html.LabelFor(x=>x.Name)
#Html.EditorFor(x=>x.Name)
</p>
<p>
#Html.LabelFor(x=>x.Price)
#Html.EditorFor(x=>x.Price)
</p>
#Html.HiddenFor(x=>x.Id)
and in your Main View, you can call this Editor template like this
#Html.EditorFor(m=>m.Products)

Resources