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?.
Related
I'm working with EF and MVC, new stuff to me.
I have the following class:
public class Client
{
[Key]
public int ClientId { get; set; }
public Category Category { get; set; }
}
and I want to show in the View related to the "Create" ActionResult, a selectable DropDownList with all the categories to choose one.
I have a ViewModel also:
public class CategoryViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
which I use in the controller:
private IEnumerable<SelectListItem> GetCategories()
{
var db = new MyDBContext();
var categories = db.Categories
.Select(x =>
new Category
{
CategoryId = x.CategoryId,
Name = x.Name
});
return new SelectList(categories, "Value", "Text");
}
...and in the Create():
public ActionResult Create()
{
var model = new CategoryViewModel();
model.Categories = GetCategories();
return View(model);
}
I dont know how to populate the dropdown in the view:
<div class="form-group">
#Html.LabelFor(model => model.Category, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(WHAT GOES HERE????)
</div>
</div>
Thanks for rescuing me! (RE5 quote)
There are different overloads but this is an option:
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories)
Well there are many questions asked on this topic and I went through many of them but unfortunately I did not found any solutions for my problem. I want to auto-populate my dropdown list of Countries, State, Cities from DB. Below is my model I designed.
Model:
public class Registration
{
[Required(ErrorMessage="Please Select a Country")]
public int CountryId { get; set; }
public IList<SelectListItem> AvailableCountries { get; set; }
[Required(ErrorMessage = "Please Select a State")]
public int StateId { get; set; }
public IList<SelectListItem> AvailableStates { get; set; }
[Required(ErrorMessage = "Please Select a City")]
public int CityId { get; set; }
public IList<SelectListItem> AvailableCities { get; set; }
}
When a new user logs in I redirect him to registration page of user Controller.
Below is my Get ActionResult of User Controller
public ActionResult Registration()
{
UserModel.Registration userreg = new UserModel.Registration();
try
{
userreg.AvailableCountries.Add(new SelectListItem { Text = "Please Select your Country", Value = "Countries" });
var query = from countries in user.Countries orderby countries select countries;
var content = query.ToList();
foreach (var country in content)
{
userreg.AvailableCountries.Add(new SelectListItem
{
Text = country.Name,
Value = country.Id.ToString()
});
}
return View(userreg);
}
catch(Exception ex)
{
Helpers.Functions_ErrorLog.LogMessage("Error from User/Registration - " + ex.Message.ToString());
}
return View(userreg);
}
This is my Registration View
#model I_am_a_Fresher.Models.UserModel.Registration
#using (Html.BeginForm("", "", FormMethod.Post, new { #id = "formstep1", #class = "active" }))
{
<div id="stepsbody" class="col-md-12 col-xs-12 col-lg-12 col-sm-12 mar-top-22">
<div class="form-group col-md-6 col-lg-6 col-sm-12 col-xs-12">
<span class="ico-state-city"></span>
#Html.DropDownListFor(model => model.CountryId, Model.AvailableCountries)
#Html.ValidationMessageFor(m => m.CountryId, null, new { #class = "text-danger error-bold" })
</div>
<div class="form-group col-md-6 col-lg-6 col-sm-12 col-xs-12">
<span class="ico-state-city"></span>
#Html.DropDownListFor(model => model.StateId, Model.AvailableStates)
#Html.ValidationMessageFor(m => m.StateId, null, new { #class = "text-danger error-bold" })
<span id="states-loading-progress" style="display: none;">Please wait..</span>
</div>
</div>
}
Why I get this error I am not clear. This should work according to me. Can anyone suggest why this error is coming?
Your problem is that you are adding SelectListItems to AvailableCountries inside a try block, but the property has not been initialized so an exception is thrown. You then return the view but AvailableCountries is still null which results in the error message you are seeing.
Either initialize it in a parameter-less constructor in the model
public class Registration
{
public Registration()
{
AvailableCountries = new List<SelectListItem>();
}
....
}
or in the controller method
UserModel.Registration userreg = new UserModel.Registration();
userreg.AvailableCountries = new List<SelectListItem>();
try
.....
However I would suggest you change the model property to
public SelectList AvailableCountries { get; set; }
and the controller to (2 lines of code vs 10)
var query = (from countries in user.Countries orderby countries select countries).ToList();
userreg.AvailableCountries = new SelectList(query, "Id", "Name");
and in the view
#Html.DropDownListFor(m => m.CountryId, Model.AvailableCountries, "Please Select your Country")
which renders the first option as "Please Select your Country" but without a value attribute which is more appropriate
Note also you will need to initialise an empty SelectList for AvailableStates and AvailableCities assuming they are being populated using javascript/jquery
userreg.AvailableStates = new SelectList(Enumerable.Empty<SelectListItem>());
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" }))
{}
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
});
I am trying to use a DropdownlistFor in MVC to add an object to my database. I am able to populate the dropdown fine, but when I post the form I get an error as the ID of the dropdown (which is a foreign key to the Team object) is populated but not the actual value. Here is my code :
[HttpGet]
public ActionResult FixtureAdd()
{
IEnumerable<SelectListItem> teams = _repository.GetTeams()
.Select(c => new SelectListItem
{
Value = c.TeamId.ToString(),
Text = c.TeamName
}).ToList();
IEnumerable<SelectListItem> weeks = _repository.GetWeeks()
.Select(c => new SelectListItem
{
Value = c.ToString(),
Text = c.ToString()
}).ToList();
ViewBag.Teams = new SelectList(teams, "Value", "Text");
ViewBag.Weeks = new SelectList(weeks, "Value", "Text");
string apiUri = Url.HttpRouteUrl("DefaultApi", new { controller = "fixture", });
ViewBag.ApiUrl = new Uri(Request.Url, apiUri).AbsoluteUri.ToString();
return View();
}
[HttpPost]
public ActionResult FixtureAdd(Fixture fx)
{
if (ModelState.IsValid)
{
try
{
//TODO: Add insert logic here
_repository.AddFixture(fx);
return RedirectToAction("FixtureAdd");
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
return View(fx);
}
}
else
{
IEnumerable<SelectListItem> teams = _repository.GetTeams()
.Select(c => new SelectListItem
{
Value = c.TeamId.ToString(),
Text = c.TeamName
}).ToList();
ViewBag.Teams = new SelectList(teams, "Value", "Text");
IEnumerable<SelectListItem> weeks = _repository.GetWeeks()
.Select(c => new SelectListItem
{
Value = c.ToString(),
Text = c.ToString()
}).ToList();
ViewBag.Weeks = new SelectList(teams, "Value", "Text");
return View(fx);
}
}
public IEnumerable<Team> GetTeams()
{
return _db.Teams.ToArray();
}
public partial class Fixture
{
public int FixtureId { get; set; }
public string Season { get; set; }
public byte Week { get; set; }
//foreign key
public int AwayTeamId { get; set; }
//navigation properties
public virtual Team AwayTeam { get; set; }
//foreign key
public int HomeTeamId { get; set; }
//navigation properties
public virtual Team HomeTeam { get; set; }
public byte? AwayTeamScore { get; set; }
public byte? HomeTeamScore { get; set; }
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Fixture</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Season)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Season)
#Html.ValidationMessageFor(model => model.Season)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Week)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Week, (SelectList)ViewBag.Weeks)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AwayTeam.TeamId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.AwayTeam.TeamId, (SelectList)ViewBag.Teams)
#Html.ValidationMessageFor(model => model.AwayTeam.TeamId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AwayTeamScore)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AwayTeamScore)
#Html.ValidationMessageFor(model => model.AwayTeamScore)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HomeTeam.TeamId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.HomeTeam.TeamId, (SelectList)ViewBag.Teams)
#Html.ValidationMessageFor(model => model.HomeTeam.TeamId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HomeTeamScore)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HomeTeamScore)
#Html.ValidationMessageFor(model => model.HomeTeamScore)
</div>
<p>
<button data-bind='click: save'>Add fixture</button>
</p>
</fieldset>
}
The TeamId foreign key field gets populated in the post but the TeamName does not. Any ideas why? This is my first attempt at this so I expect I am possibly making more than one error with this.
MVC, unlike traditional ASP, is light-weight and doesn't store all those objects in a hidden field. That is to say, if you had the following:
class Customer {
public Int32 Id { get; set; }
public String Name { get; set; }
}
In normal ASP you could map a collection of these objects to a dropdown list and on the post back you'd get the original object that was referenced in the result. MVC doesn't work this way because it's not storing these objects any more. So if you want that intended object you're going to need to use the passed ID and re-retrieve the desired result.
Picture the following EditorTemplate for a CustomerId property on your model:
#model Int32
#Html.DropDownListFor(x => x, new SelectList(
Customers.GetAllCustomers().Select(y => new SelectListItem {
Value = y.Id,
Text = y.Name,
Selected = Model == y.Id
})
);
And of course:
#Html.EditorFor(x => x.CustomerId) #* Use above EditorTemplate *#
Then in your submit action:
[HttpPost]
public ActionResult(MyViewModel model)
{
if (ModelState.IsValid)
{
// ID was passed, re-fetch the customer based on selected id
Customer customer = Customers.GetById(model.CustomerId)
/* ... */
}
/* ... */
return View();
}