I'm having a real hard time trying to set up a drop-down list to a related model/table in my create view. My Guest model has a reference to the PersonPrefix model in the following way:
Guest model:
public virtual PersonPrefix Prefix { get; set; }
PersonPrefix model:
public class PersonPrefix
{
[Key]
public int PersonPrefixID { get; set; }
[StringLength(6)]
public string Abbreviation { get; set; }
[StringLength(255)]
public string Name { get; set; }
public virtual ICollection<Guest> Guests { get; set; }
}
I have done the following to be able to get the data from the database and show it in a dropdown:
Controller:
public ActionResult Create()
{
ViewBag.PersonPrefixes = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
return View();
}
and I've added the prefix object to the post
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate,SelectedPrefix")] Guest guest)
{
if (ModelState.IsValid)
{
db.Guests.Add(guest);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(guest);
}
This is the relevant code in the view so far but it is not passing the value to the controller:
<div class="form-group">
@Html.LabelFor(model => model.Prefix, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10" >
@*@Html.DropDownListFor(m => m.Prefix, new SelectList(ViewBag.PersonPrefixes, "Value", "Text", 1))*@
@Html.DropDownListFor(m => m.Prefix,
new SelectList(ViewBag.PersonPrefixes, "Value", "Text", 1))
</div>
</div>
Thanks for your help!!
You cannot bind a <select>
element to a complex object. All html form controls post back a single value (or in the case of <select multiple>
an array of value types). If you selected an option with a value of (say) 5, then the DefaultModelBinder
would try to set guest.Prefix = 5;
which of course fails.
You need to bind to a value type (e.g. int
, string
etc). In your case you cannot even bind to the PersonPrefixID
of PersonPrefix
because validation would fail on the other properties of PersonPrefix
. As always when editing, you should use a view model containing only those properties you need to edit.
public class GuestVM
{
[Display(Name = "Prefix")]
[Required(ErrorMessage = "Please select a prefix")]
public int SelectedPrefix { get; set; }
.... // other properties of Guest
public SelectList PrefixList { get; set; }
}
Controller
public ActionResult Create()
{
GuestVM model = new GuestVM();
model.PrefixList = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
.... // set other properties as required
return View(model); // always return an instance of the model
}
View
@Html.LabelFor(m => m.SelectedPrefix)
@Html.DropDownListFor(m => m.SelectedPrefix, Model.PrefixList, "--please select--")
@Html.ValidationMessageFor(m => m.SelectedPrefix)
Then in the POST method, initialize a new instance of Guest
data model and map its properties from the posted view model and finally save the data model.
public ActionResult Create(GuestVM model)
{
if (!ModelSTate.IsValid)
{
model.PrefixList = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
return View(model);
}
// Initialize a new instance of the data model and set its properties
Guest guest = new Guest()
{
FirstName = model.FirstName,
MiddleName = model.MiddleName,
.... // other properties
Prefix = db.PersonPrefixes.Find(model.SelectedPrefix)
};
db.Guests.Add(guest);
db.SaveChanges();
return RedirectToAction("Index");
}
Side note: You do not need to create another SelectList
in the view (its already a SelectList
) and the last parameter where you tried to set the selected value to 1
is ignored (its the value of the property your binding to which determines which option is selected) so if you want to pre-select an option with value="1"
, then set the value of SelectedPrefix = 1;
in the controller before you pass the model to the view.