带有 ViewBag 的 MVC DropDownListFor 未将值传递给控制器
MVC DropDownListFor with ViewBag not passing value to controller
我很难尝试在我的创建视图中为相关 model/table 设置下拉列表。我的 Guest 模型通过以下方式引用了 PersonPrefix 模型:
客座模特:
public virtual PersonPrefix Prefix { get; set; }
PersonPrefix 模型:
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; }
}
为了能够从数据库中获取数据并将其显示在下拉列表中,我已经完成了以下操作:
控制器:
public ActionResult Create()
{
ViewBag.PersonPrefixes = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
return View();
}
并且我已将前缀对象添加到 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);
}
到目前为止,这是视图中的相关代码,但它没有将值传递给控制器:
<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>
感谢您的帮助!!
您不能将 <select>
元素绑定到复杂对象。所有 html 表单控件 post 返回单个值(或者在 <select multiple>
的情况下返回值类型数组)。如果您 select 编辑了一个值为(比如)5 的选项,那么 DefaultModelBinder
将尝试设置 guest.Prefix = 5;
这当然会失败。
您需要绑定到一个值类型(例如 int
、string
等)。在您的情况下,您甚至不能绑定到 PersonPrefix
的 PersonPrefixID
,因为验证会在 PersonPrefix
的其他属性上失败。与往常一样,在编辑时,您应该使用仅包含需要编辑的那些属性的视图模型。
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; }
}
控制器
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
}
查看
@Html.LabelFor(m => m.SelectedPrefix)
@Html.DropDownListFor(m => m.SelectedPrefix, Model.PrefixList, "--please select--")
@Html.ValidationMessageFor(m => m.SelectedPrefix)
然后在POST方法中,初始化Guest
数据模型的新实例,并从posted视图模型映射其属性,最后保存数据模型。
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");
}
旁注:您不需要在视图中创建另一个 SelectList
(它已经是 SelectList
)和您尝试设置 selected 值的最后一个参数to 1
被忽略(它的 属性 的值决定了哪个选项是 selected)所以如果你想预先 select 一个带有 value="1"
,然后在将模型传递给视图之前,在控制器中设置 SelectedPrefix = 1;
的值。
虽然根据 Stephen 的回答,最好使用 ViewModels,但您在这里具体做错的是:
首先,你的DropDownListFor是错误的。您的 ViewBag 条目已包含一个 SelectList,因此您无需再创建一个...您只需转换动态类型即可。
@Html.DropDownListFor(m => m.Prefix.PersonPrefixID,
(SelectList)ViewBag.PersonPrefixes, "Select...")
第一个参数是从 PersonPrefixes 中的对象列表中选择的 ID...因此当模型 posted 到控制器时,它将包含 ID(但不包含其他数据,因此您必须确保查询数据库以填充它。
其次,您需要将 Post 操作更改为:
Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate")] Guest guest)
然后在您的 PersonPrefix class 上使用绑定语法:
[Bind(Include = "PersonPrefixID")]
public class PersonPrefix
{
[Key]
public int PersonPrefixID { get; set; }
.....
}
现在,当您 post 时,PersonPrefix 的其余部分将为空,因为此数据不存在于 post 中,因此您必须使用 [=] 从数据库中检索它29=]ed PersonPrefixID.
但是,正如我所说,最好使用 ViewModel。即便如此,我在这里所说的某些内容仍然适用。您的 DropDownListFor 需要正确,并使用选定的 ID 属性。而且,如果您使用的是 ViewModel,则没有理由使用 Bind 属性,因为您只是绑定了您需要的确切项。
我很难尝试在我的创建视图中为相关 model/table 设置下拉列表。我的 Guest 模型通过以下方式引用了 PersonPrefix 模型:
客座模特:
public virtual PersonPrefix Prefix { get; set; }
PersonPrefix 模型:
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; }
}
为了能够从数据库中获取数据并将其显示在下拉列表中,我已经完成了以下操作:
控制器:
public ActionResult Create()
{
ViewBag.PersonPrefixes = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
return View();
}
并且我已将前缀对象添加到 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);
}
到目前为止,这是视图中的相关代码,但它没有将值传递给控制器:
<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>
感谢您的帮助!!
您不能将 <select>
元素绑定到复杂对象。所有 html 表单控件 post 返回单个值(或者在 <select multiple>
的情况下返回值类型数组)。如果您 select 编辑了一个值为(比如)5 的选项,那么 DefaultModelBinder
将尝试设置 guest.Prefix = 5;
这当然会失败。
您需要绑定到一个值类型(例如 int
、string
等)。在您的情况下,您甚至不能绑定到 PersonPrefix
的 PersonPrefixID
,因为验证会在 PersonPrefix
的其他属性上失败。与往常一样,在编辑时,您应该使用仅包含需要编辑的那些属性的视图模型。
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; }
}
控制器
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
}
查看
@Html.LabelFor(m => m.SelectedPrefix)
@Html.DropDownListFor(m => m.SelectedPrefix, Model.PrefixList, "--please select--")
@Html.ValidationMessageFor(m => m.SelectedPrefix)
然后在POST方法中,初始化Guest
数据模型的新实例,并从posted视图模型映射其属性,最后保存数据模型。
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");
}
旁注:您不需要在视图中创建另一个 SelectList
(它已经是 SelectList
)和您尝试设置 selected 值的最后一个参数to 1
被忽略(它的 属性 的值决定了哪个选项是 selected)所以如果你想预先 select 一个带有 value="1"
,然后在将模型传递给视图之前,在控制器中设置 SelectedPrefix = 1;
的值。
虽然根据 Stephen 的回答,最好使用 ViewModels,但您在这里具体做错的是:
首先,你的DropDownListFor是错误的。您的 ViewBag 条目已包含一个 SelectList,因此您无需再创建一个...您只需转换动态类型即可。
@Html.DropDownListFor(m => m.Prefix.PersonPrefixID,
(SelectList)ViewBag.PersonPrefixes, "Select...")
第一个参数是从 PersonPrefixes 中的对象列表中选择的 ID...因此当模型 posted 到控制器时,它将包含 ID(但不包含其他数据,因此您必须确保查询数据库以填充它。
其次,您需要将 Post 操作更改为:
Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate")] Guest guest)
然后在您的 PersonPrefix class 上使用绑定语法:
[Bind(Include = "PersonPrefixID")]
public class PersonPrefix
{
[Key]
public int PersonPrefixID { get; set; }
.....
}
现在,当您 post 时,PersonPrefix 的其余部分将为空,因为此数据不存在于 post 中,因此您必须使用 [=] 从数据库中检索它29=]ed PersonPrefixID.
但是,正如我所说,最好使用 ViewModel。即便如此,我在这里所说的某些内容仍然适用。您的 DropDownListFor 需要正确,并使用选定的 ID 属性。而且,如果您使用的是 ViewModel,则没有理由使用 Bind 属性,因为您只是绑定了您需要的确切项。