When clicking save to submit my form, my HTTPPost Edit Action receives a null value for IEnumerable<MedicalProduct> productList. I am not sure what parameter to use for my edit Action to prevent nulls. I would try and determine the cause with some breakpoints but cannot find the spot before the parameter is assigned null.
Side Note:
I am adapting my code to use this new Model/ViewModel hierarchy. My Controller hasn't been completely tested with these new models, but I started testing the Edit Action, and received the null reference exception when trying to use the parameter IEnumerable<MedicalProduct> productList in my Edit post action.
Another Side Note:
I am using a sub-ViewModel class MedicalProductViewModelLineItem (haven't figured out a better name yet) inside my ViewModel MedicalProductViewModel because I need a way to retrieve all the Brand names from the database with one database call, and then assign them one by one to the MedicalProductViewModelLineItem.
EDIT: CODE UPDATE 10/22/13 5:14pm CST. The values produced in FormCollection.Keys parameter in the HttpPost Edit action method are now fixed. Now, values like "Products[0].Name" or "Products[0].ID" are produced in FormCollection.Keys parameter instead of "p.Name" or "Name". However, the productList parameter is still null.
Model Classes
MedicalProductViewModel
public class MedicalProductViewModel
{
public List<MedicalProductViewModelLineItem> Products { get; private set; }
//public SelectListItem BrandSelectListItem { get; private set; }
public void BuildViewModel(IEnumerable<MedicalProductViewModelLineItem> productsList, IEnumerable<Brand> brandList)
{
BuildProducts(productsList, brandList);
}
public void BuildViewModel(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
{
BuildProducts(productsList, brandList);
}
private IEnumerable<SelectListItem> BuildSelectListItems(IEnumerable<Brand> brandList)
{
return brandList.Select(b => new SelectListItem()
{
Text = b.Name,
Value = b.ID.ToString()
});
}
private void BuildProducts(IEnumerable<MedicalProductViewModelLineItem> productList, IEnumerable<Brand> brandList)
{
var medicalProducts = productList.Select(p => new MedicalProduct()
{
BrandID = p.BrandID,
ID = p.ID,
Name = p.Name,
Price = p.Price
});
BuildProducts(medicalProducts, brandList);
}
private void BuildProducts(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
{
Products = productsList.Select(p => new MedicalProductViewModelLineItem()
{
BrandID = p.BrandID,
BrandName = brandList.Single(b => b.ID == p.BrandID).Name,
BrandSelectListItem = BuildSelectListItems(brandList),
ID = p.ID,
Name = p.Name,
Price = p.Price
}).ToList();
}
}
MedicalProductViewModelLineItem
// Sub-ViewModel of MedicalProductViewModel
// It gets displayed as one row on a view.
public class MedicalProductViewModelLineItem
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
// is a foreign key
public int BrandID { get; set; }
public string BrandName { get; set; }
}
MedicalProduct
// DOMAIN MODEL
public class MedicalProduct
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
// is a foreign key
public int BrandID { get; set; }
}
Controller
MedicalProductController
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb()
//
// GET: /MedicalSupply/Edit/5
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModel(productList);
return View(viewModel);
}
// ==========================================
// NULL REFERENCE EXCEPTION OCCURS IN THIS ACTION
// ==========================================
// POST: /MedicalSupply/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IEnumerable<MedicalProductViewModelLineItem> productList, FormCollection values)
{
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productViewModelList = GetMedicalProductViewModel(productList);
return View(productViewModelList);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
Views
Edit.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#for (int i = 0; i < Model.Products.Count(); i++)
{
#Html.EditorFor(m => m.Products[i])
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
EditorTemplates\MedicalProductViewModelLineItem.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModelLineItem
#Html.HiddenFor(item => Model.ID)
<div class="editor-label">
#Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Name)
#Html.ValidationMessageFor(item => Model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Price)
#Html.ValidationMessageFor(item => Model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.BrandID)
</div>
<div class="editor-field">
#Html.DropDownListFor(item => Model.BrandID, Model.BrandSelectListItem)
#Html.ValidationMessageFor(item => Model.BrandID)
</div>
EDIT: (Pictures obsolete)
two images of the foreach approach, and for approach used in MedicalProductViewModel.cshtml, and the resulting key values of FormsCollection parameter values
Use a BindAttribute with Prefix in your Controller Action like below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Prefix = "Products")] IEnumerable<MedicalProductViewModelLineItem> productList)
{
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productViewModelList = GetMedicalProductViewModel(productList);
return View(productViewModelList);
}
Change your view from foreach loop to for loop
#foreach (MvcMedicalStore.Models.MedicalProductViewModelLineItem p in Model.Products)
{
#Html.HiddenFor(item => p.ID)
//rest of the code.
}
to
#for ( i = 0; i < Model.count(); i++)
{
#Html.LabelFor(m => m[i].Name)
#Html.HiddenFor(m => m[i].Name)
#Html.LabelFor(m => m[i].Price)
#Html.EditorFor(m => m[i].Price)
//rest of the code.
}
[HttpPost]
public MedicalProductViewModel GetMedicalProductViewModel(ICollection<MedicalProduct> productList)
{
var brandList = _db.Brands.ToArray();
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductViewModel(productList, brandList);
}
Make it more explicit, specify
FormMethod.Post
#using (Html.BeginForm()) { } to
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { id = "forId" }))
{}
Related
Can someone help me to know why model.Prodis null like in the picture below and model.catg is not null? There are items in my Product table. And also after i add #Html.HiddenFor(p=>p.Prod[i].id) to the view, model.Prod is not null but only id has value. Other parameters are still null.
#model HandMShop.ViewModel.HMViewM
#using (Html.BeginForm("ShopPage", "Shop", FormMethod.Post)) {
#for(int i=0;i< Model.catg.Count;i++) {
#Html.CheckBoxFor(model => model.catg[i].IsChecked)
<label>#Model.catg[i].CategoryName</label>
#Html.HiddenFor(model => model.catg[i].id)
#Html.HiddenFor(model => model.catg[i].CategoryName }
#for (int i = 0; i < Model.Prod.Count; i++) {
<div class="col-md-6 col-lg-4">
<figure class="card card-product mehsul">
<div class="img-wrap"> <img class="img-fluid mehsulimg"
src="#Model.Prod[i].PhotoProducts.First().ImageName" alt=""> </div>
<div class="handhover">
<img class="img-fluid" src="#Model.Prod[i].PhotoProducts.Last().ImageName" alt="">
</div>
<figcaption class="info-wrap">
<h4 class="title">#Model.Prod[i].ProdName</h4>
<p class="desc">#Model.Prod[i].Description</p>
</figcaption>
</figure>
</div>
}
View Model I used:
public class HMViewM
{
public List<User> userr { get; set; }
public List<homesec1slider> homesec1 { get; set; }
public List<Category> catg { get;set; }
public List<Colour> colrs { get; set; }
public List<PhotoProduct> Photopr { get; set; }
public List<Product> Prod { get; set; }
public bool Istehsal { get; set; }
}
Controller
[HttpPost]
public ActionResult ShopPage(HMViewM model)
{
List<Category> selectedcatg = model.catg.Where(x => x.IsChecked == true).ToList();
List<Product> selectedprod = model.Prod.Where(i => i.CategoryId == selectedcatg.FirstOrDefault().id).ToList();
var vm = new HMViewM
{
Prod = selectedprod,
catg = _context.Categories.ToList(),
colrs = _context.Colours.ToList(),
Photopr = _context.PhotoProducts.Where(ph => ph.Product.id == ph.ImageId).ToList(),
};
return View(vm);
}
That's because after page being postbacked by clicking submit button, only those properties will be return to your controller that you explicitly bind them to html input elements.
Values that binded in your code is:
#Html.CheckBoxFor(model => model.catg[i].IsChecked)
#Html.HiddenFor(model => model.catg[i].id)
#Html.HiddenFor(model => model.catg[i].CategoryName }
So only these values filled in your controller HMViewM object.
If you want other values you should bind them to html input elements too (using hidden field, text, checkbox ...)
I have seen lots of examples on this, but cannot get any working. I have built this example to prove/disprove passing back of the view model SomeDataViewModel.
I am trying to post back the dropdownlist data. Everything works ok, but the OtherData property on TestViewModel never returns the collect that was passed in.
Have tried adding:
#Html.HiddenFor(m => Model.OtherData)
but again this just produces the following error;
The parameter conversion from type 'System.String' to type 'SomeDataViewModel' failed because no type converter can convert between these types
The Code:
ViewModels
TestViewmodel
public class TestViewModel
{
public TestViewModel()
{
OtherData = new List<SomeDataViewModel>();
}
public int Id { get; set; }
public String Name { get; set; }
public DateTime DoB { get; set; }
public int SelectedOtherData { get; set; }
public List<SomeDataViewModel> OtherData { get; set; }
public IEnumerable<SelectListItem> TolistData()
{
IEnumerable<SelectListItem> ret = OtherData.Select(i => new SelectListItem() { Text = i.Text, Value=i.Value });
return ret;
}
}
SomeDataViewmodel
public class SomeDataViewModel
{
public string Value { get; set; }
public string Text { get; set; }
}
View
#model TestViewModel
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm("Index","Home"))
{
<div class="row">
<div class="col-md-12">
<br />
#Html.EditorFor(m => Model.Id)
<br />
#Html.EditorFor(m => Model.Name)
<br />
#Html.EditorFor(m => Model.DoB)
<br/>
#Html.DropDownListFor(m => Model.SelectedOtherData, Model.TolistData(), new { id = "OtherData" })
<p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301865">Learn more ยป</a></p>
</div>
</div>
<button id="dosomething" formmethod="post">Post</button>
}
Controller
public ActionResult Index()
{
var model = new TestViewModel() {
Id = 99,
Name = "Billy",
DoB = DateTime.Now
};
model.OtherData.Add(
new SomeDataViewModel { Text = "Bob", Value = "1" });
model.OtherData.Add(
new SomeDataViewModel { Text = "Sally", Value = "2" });
return View(model);
}
[HttpPost]
public ActionResult Index(TestViewModel retModel)
{
if (ModelState.IsValid)
{
if (retModel.OtherData.Count() == 0)
{
var dud = true;
}
}
return View(retModel);
}
You can't render hidden inputs for complex data with #Html.HiddenFor helper.
You should use it only with simple types. Becouse you got array you should write something like this:
#for(int i = 0; i < Model.OtherData.Count(); i++)
{
#Html.HiddenFor(m => Model.OtherData[i].Text)
#Html.HiddenFor(m => Model.OtherData[i].Value)
//... other fields.
#Html.HiddenFor(m => Model.OtherData[i].OtherProperty)
}
Use for loop instead of foreach becouse you should same your mappings for right binding on form POST.
Certainly there is a type conversion error. your SelectedOtherData is type of int while selectlistitem value is type of string
i'm new to asp.net mvc and i would like to know why ModelState.IsValid=false ??
in edit view when using
if (ModelState.IsValid)
{
//code
}
the complete code listed here:
model classes:
public class Departments
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employeesss { get; set; }
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string City { get; set; }
public int DepartmentId { get; set; }
[ForeignKey("DepartmentId")]
public virtual Departments Id { get; set; }
}
public class dropdownDbContext : DbContext
{
public DbSet<Departments> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
in controller these are the edit controller
public ActionResult Edit(int id = 0)
{
Employee employee = db.Employees.Find(id);
if (employee == null)
{
return HttpNotFound();
}
ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
return View(employee);
}
//
// POST: /empttttttttttttttttt/Edit/5
[HttpPost]
public ActionResult Edit(Employee employee)
{
if (!ModelState.IsValid)
{
var errors = ModelState.SelectMany(x => x.Value.Errors.Select(z => z.Exception));
// Breakpoint, Log or examine the list with Exceptions.
}
if (ModelState.IsValid)
{
db.Entry(employee).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.DepartmentId = new SelectList(db.Departments, "Id", "Name", employee.DepartmentId);
return View(employee);
}
in view
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Employee</legend>
#Html.HiddenFor(model => model.EmployeeId)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DepartmentId, "Id")
</div>
<div class="editor-field">
#Html.DropDownList("DepartmentId", String.Empty)
#Html.ValidationMessageFor(model => model.DepartmentId)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
it doesn't show error but the edit didn't work properly in this logic when using
if (ModelState.IsValid)
{
db.Entry(employee).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
and i would like to know why ModelState.IsValid=false ??
this is error snapshot
The parameter conversion from type 'System.String' to type 'dropdown.Models.Departments' failed because no type converter can convert between these types.
Change:
if (!ModelState.IsValid)
{
var errors = ModelState.SelectMany(x => x.Value.Errors.Select(z => z.Exception));
// Breakpoint, Log or examine the list with Exceptions.
}
To:
if (!ModelState.IsValid)
{
var errors = ModelState
.Where(x => x.Value.Errors.Count > 0)
.Select(x => new { x.Key, x.Value.Errors })
.ToArray();
}
Then you put your breakpoint on errors instead
EDIT:
Change your model like this:
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string City { get; set; }
[ForeignKey("Departments")]
public int DepartmentId { get; set; }
public virtual Departments department { get; set; }
}
I'm trying to make very simple forum, but I have problem with DropDownList. I have two models:
ForumThread.cs
public partial class ForumThread
{
public ForumThread()
{
this.ForumCategory = new HashSet<ForumCategory>();
}
public int TH_ID { get; set; }
public System.DateTime DATE { get; set; }
public string TOPIC { get; set; }
public string USER { get; set; }
public virtual ICollection<ForumCategory> ForumCategory { get; set; }
}
ForumCategory.cs
public partial class ForumCategory
{
public ForumCategory()
{
this.ForumThread = new HashSet<ForumThread>();
}
public int CA_ID { get; set; }
public string CATEGORY { get; set; }
public bool isSelected { get; set; }
public virtual ICollection<ForumThread> ForumThread { get; set; }
}
I tried to make "Create" function with view:
Create
#model AnimeWeb.Models.ForumThread
#{
ViewBag.Title = "Create";
}
<h2>New Thread</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-field">
#Html.HiddenFor(model => model.TH_ID)
</div>
<div class="editor-label">
TOPIC
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TOPIC)
#Html.ValidationMessageFor(model => model.TOPIC)
</div>
<div class="editor-label">
CATEGORY
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ForumCategory)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And PartialView for ForumCategory:
ForumCategory
#model AnimeWeb.Models.FORUMCATEGORY
#Html.HiddenFor(model => model.CA_ID)
#Html.HiddenFor(model => model.CATEGORY)
<div>
#Html.DropDownListFor(item => Model.CA_ID, ViewBag.CA_ID as SelectList, "-- Select --")
</div>
ForumController
public ActionResult Create()
{
var db = new MainDatabaseEntities();
var viewModel = new ForumThread
{
ForumCategory = db.ForumCategory.Select(c => new { CA_ID = c.CA_ID, CATEGORY = c.CATEGORY, isSelected = false }).ToList().Select(g => new ForumCategory
{
CA_ID = g.CA_ID,
CATEGORY = g.CATEGORY,
isSelected = false
}).ToList(),
};
return View(viewModel);
}
//
// POST: /Forum/Create
[HttpPost]
public ActionResult Create(ForumThread forumthread, String user, int id)
{
var db = new MainDatabaseEntities();
var newthread = new ForumThread
{
TH_ID = forumthread.TH_ID,
DATE = DateTime.Now,
TOPIC = forumthread.TOPIC,
USER = forumthread.USER,
ForumCategory = new List<ForumCategory>()
};
foreach (var selectedCategory in forumthread.FORUMCATEGORY.Where(c => c.isSelected))
{
var category = new ForumCategory { CA_ID = selectedCategory.CA_ID };
db.ForumCategory.Attach(category);
newthread.ForumCategory.Add(category);
}
db.ForumThread.Add(newthread);
db.SaveChanges();
return RedirectToAction("Index");
}
And it obviously doesn't work. I tried to use other threads on this forum but nothing helped. Could someone explain me how to make this work?
The error is in partial view of ForumCategory:
The ViewData item that has the key 'CA_ID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
In your PartialView for ForumCategory, your cast is not correct:
#Html.DropDownListFor(item => Model.CA_ID, ViewBag.CA_ID as SelectList, "-- Select --")
You have to use a SelectList (List of SelectListItem) that you can implement for example in a method in your model:
public List<SelectListItem> GetCategories()
{
var db = new MainDatabaseEntities();
List<SelectListItem> list = new List<SelectListItem>();
// Add empty item if needed
SelectListItem commonItem = new SelectListItem();
commonItem.Text = "--- Select ---";
commonItem.Value = "-1";
commonItem.Selected = true;
list.Add(commonItem);
// Add items from Database
foreach (ForumCategory fc in db.ForumCategory)
{
SelectListItem i = new SelectListItem();
i.Text = fc.CATEGORY;
i.Value = fc.CA_ID.ToString();
list.Add(i);
}
return list;
}
And then you can have you dropdown like that:
#Html.DropDownList("DropName", Model.GetCategories())
There may be other errors in some parts of your code, I just answered to the one you quoted
In your editortemplate, you have:
ViewBag.CA_ID as SelectList
But you don't show where you fill the ViewBag. Instead you might want to do something like this:
#Html.DropDownListFor(m => m.CA_ID,
new SelectList(Model.ForumCategory,
"CA_ID", "CATEGORY", Model.CA_ID))
As also explained in MVC3 DropDownListFor - a simple example?.
I am trying to put a list of medical product brand names in a dropdown list on my edit.cshtml file using Html.DropDownListFor(). I am trying to use the Html helper properly but my efforts have been unsuccessful. Model.BrandSelectListItem is an IEnumerable that is supposed to go into the DropDownList.
Also, I don't understand why I need to reference the model as 'model' in the lambda expression, but as 'Model' in the second argument of the following code segment:
#Html.DropDownListFor(model => model.BrandName, Model.BrandSelectListItem)
when executing the code, I receive the following run-time error:
The ViewData item that has the key 'BrandName' is of type
'System.String' but must be of type 'IEnumerable'.
Here are some classes that may be relevant to this error:
MedicalProduct
public class MedicalProduct
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
// is a foreign key
public Nullable<int> BrandID { get; set; }
}
MedicalProductViewModel
public class MedicalProductViewModel
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
public Nullable<int> BrandID { get; set; }
[DisplayFormat(NullDisplayText="[Generic]")]
public string BrandName { get; set; }
public IEnumerable<SelectListItem> BrandSelectListItem { get; set; }
}
MedicalProductController
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
//
// GET: /MedicalSupply/Edit/5
public ActionResult Edit(int id = 0)
{
MedicalProduct medicalProduct = _db.Products.Find(id);
if (medicalProduct == null)
{
return HttpNotFound();
}
var viewModel = GetMedicalProductViewModel(medicalProduct);
return View(viewModel);
}
public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct product)
{
var mapper = new MedicalProductMapper(_db);
return mapper.GetMedicalProductViewModel(product);
}
}
MedicalProductMapper
public class MedicalProductMapper
{
private MvcMedicalStoreDb _db;
public MedicalProductMapper(MvcMedicalStoreDb db)
{
_db = db;
}
public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct source)
{
MedicalProductViewModel viewModel = new MedicalProductViewModel();
var dbBrands = _db.Brands.ToArray();
viewModel.ID = source.ID;
viewModel.Name = source.Name;
viewModel.Price = source.Price;
viewModel.BrandID = source.BrandID;
if (viewModel.BrandID != null)
viewModel.BrandName = dbBrands.SingleOrDefault(b => b.ID == source.BrandID).Name;
var queryBrands = from b in dbBrands
select b;
// BrandSelectListItem is 'null' before this assignment statement executes.
// and Stays null after the statement executes, why?
viewModel.BrandSelectListItem = queryBrands as IEnumerable<SelectListItem>;
return viewModel;
}
}
Edit.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.BrandName)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.BrandName, Model.BrandSelectListItem)
#Html.ValidationMessageFor(model => model.BrandName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Also, I don't understand why I need to reference the model as 'model'
in the lambda expression, but as 'Model' in the second argument of the
following code segment:
You don't need to reference it as model in the lambda expression. The first part of the lambda (before the => symbol) you are just specifying your parameters and you can name them whatever you want
anything => anything.BrandName
would work the same as using model in place of anything
The lambda expression in this case you can think of it as a method that looks like this
TProperty GetPropertyYouWantToCreateADropdownFor(MedicalPropertyViewModel model)
{
return model.BrandName
}
As for the dropdown, here is a good post explaining how to create a dropdown
How to write a simple Html.DropDownListFor()?
Edit
I plugged in your all your code except your mapper, and just made a little fake implementation of your ViewModel. Your code worked fine.
Here's what I used for my fake controller
var viewModel = new MedicalProductViewModel
{
BrandID = 12,
BrandName = "Some Brand",
ID = 6,
Name = "Some Name",
Price = 12.27,
BrandSelectListItem = new List<SelectListItem>()
{
new SelectListItem() {Text = "Item 1", Value = "1"},
new SelectListItem() {Text = "Item 2", Value = "2"}
}
};
return View(viewModel);
So seeing that this worked, the only thing I can guess is that your problem lies in your Mapper, can you give an example of what viewModel.BrandSelectListItem equals after you get data from the database? Does throwing in fake data work for you?
Edit 2:
queryBrands has to be compatible with SelectListItem if your going to cast it into an IEnumerable<SelectListItem>
You can modify your existing
var queryBrands = from b in dbBrands
select b;
to something like
var queryBrands = dbBrands.Select(b => new SelectListItem()
{
Text = b.PropertyWithWhatYouWantToDisplay,
Value = b.PropertyYouWantAsTheValue,
Selected = //true if you want this item selected, false otherwise
});