我目前可以使用以下模型注释,视图模型和自定义编辑器模板填充并显示下拉列表 - 但是在持续提交时遇到问题(更多内容见下文):
//Domain
Public class Person
{
public string FirstName {get;set;}
public string LastName {get;set;}
public string ClassName {get;set;}
public int ClassId {get;set;}
...
}
//ViewModel
Public class PersonViewModel
{
public Person Person {get;set;}
[UIHint("SelectList")]
public IEnumerable<SelectListItems> Classes {get;set;}
public PersonViewModel
{
Person = new Person();
}
}
//Controller
public ActionResult Create()
{
var PersonViewModel = new PersonViewModel();
_PersonService.PopulateClassList(PersonViewModel);
return View("Create", PersonViewModel);
}
[HttpPost]
public ActionResult Create(PersonViewModel PersonViewModel)
{
try
{
if (!ModelState.IsValid)
{
_PersonService.PopulateClassList(PersonViewModel);
return View("Create", PersonViewModel);
}
var Person = new Person();
Person.InjectFrom(PersonViewModel.Person);
_PersonService.Save(Person);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//View (just the pertinent part)
@Html.EditorFor(m => m.Classes)<br />
@Html.EditorFor(m => m.Person)
由于viewmodel注释
,会自动使用以下模板// Views/Shared/EditorTemplates/SelectList.cshtml
@model IEnumerable<SelectListItem>
@Html.DropDownListFor(m => m, Model)
就像我提到的,上面工作正常 - 因为在下拉列表中显示正确,但显然在当前状态下它没有将classID(或名称)保存到Person实体,我理解问题但不知道如何解决它。
我的问题:
如何将所选值从下拉列表保存到Person实体
如何将ID和名称保存到Person实体中 - 我猜测只有一个隐藏的字段可以填充onchange事件(这听起来很奇怪,我确定,但它是存储在文档DB中,因此它没有标准化 - 我最后可能只存储Name)
提前致谢! 麦克
答案 0 :(得分:3)
我认为在您的viewmodel中,您需要更改代码以使Classes为
IEnumerable<SelectListItem>
不是<SelectListItems>
,您需要为所选项目添加字段。
Public class PersonViewModel
{
public Person Person {get;set;}
public IEnumerable<SelectListItem> Classes {get;set;}
public string SelectedClassId {get; set;}
}
然后,您可以在视图中使用这样的代码来填充下拉列表。
@Html.DropDownListFor(x => x.SelectedClassId, Model.Classes, new { id = "classSelect" })
[编辑]
我仔细看了一下你正在做什么,看起来你正在创建一个表单来为新人输入信息,然后为那个人选择一个类,然后创建新的Person并保持它们。
首先,您不应该将PersonViewModel传递给您的服务。视图模型不是您的域的一部分。它应该用于促进View和Controller之间的通信。
其次,由于你还没有Person,你的_PersonService.PopulateClassList()方法应该更像这样。
public IQueryable<Class> GetClasses()
{
//Data access logic
//return classes .AsQueryable()
}
这样,您可以在需要时获取类列表。你的控制器看起来就像这样。
[HttpPost]
public ActionResult Create(PersonViewModel model)
{
try
{
if (!ModelState.IsValid)
{
model.Classes = _PersonService.GetClasses().Select(c => new SelectListItem
{
Text = c.Name,
Value = "" + c.Id
};
return View("Create", PersonViewModel);
}
model.Person.ClassId = model.SelectedClassId;
//You can ditch ClassName since you'll just look it up by ID
_PersonService.Save(model.Person);
return RedirectToAction("Index");
}
[回答您的问题]
如果要从控制器中获取SelectList逻辑,可以修改模型中的属性,使其看起来像这样。
Public class PersonViewModel
{
public Person Person {get;set;}
public string SelectedClassId {get; set;}
public IEnumerable<SelectListItem> Classes
{
get
{ return _PersonService.GetClasses().Select(c => new SelectListItem
{
Text = c.Name,
Value = "" + c.Id
};
}
}
}
这将在读取该属性时访问该服务。我无法确定,但你可能需要在最后调用.AsEnumerable()。
我正在玩下拉框,看看我是否可以绑定到模型中的复杂对象。更多信息。
<强> [持续] 强>
是的,您可以将下拉值直接绑定到模型中Person的ClassId属性。我能够在我的模型/视图中使用以下代码来完成它。我的Person和Class对象可能与你的完全不同,但你会明白这一点。
另外,我不确定你的域名,但是如果一个类可以在没有人的情况下存在,那么将它们的数据访问逻辑分成PersonRepository和ClassRepository(或PersonService等等)可能更有意义。 ,但我会把域式思维留给你。
<强>模型强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DropDownTest.Classes;
using System.Web.Mvc;
namespace DropDownTest.Models
{
public class PersonViewModel
{
private ClassRepository _classRepository = new ClassRepository();
public Person Person { get; set; }
public IEnumerable<SelectListItem> Classes
{
get
{
return _classRepository.GetClasses().Select(c => new SelectListItem
{
Text = c.Name,
Value = "" + c.ClassId
});
}
}
}
}
查看强>
@model DropDownTest.Models.PersonViewModel
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm("Create", "Person", FormMethod.Post))
{
<fieldset>
<legend>New Person</legend>
@Html.LabelFor(c => c.Person.FirstName)
@Html.TextBoxFor(c => c.Person.FirstName)
<br />
@Html.LabelFor(c => c.Person.LastName)
@Html.TextBoxFor(c => c.Person.LastName)
<br />
@Html.LabelFor(c => c.Person.ClassId)
@Html.DropDownListFor(c => c.Person.ClassId, Model.Classes, new { id = "classSelect" })
<input type="submit" value="submit" />
</fieldset>
}
类库(在这种情况下将其视为服务) 这显然是假的,但是如果你使用抽象存储库,你可以在单元测试中使用这样的东西,并使用你的真实存储库进行生产使用,只要实现了IClassRepository并且你使用了某种依赖注入(DI) - 就像Ninject一样 - 但这是一个完全不同的主题:)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DropDownTest.Classes
{
public class ClassRepository
{
private static IQueryable<Class> _classes = new List<Class>
{
new Class { ClassId = 0, Name = "Class 0" },
new Class { ClassId = 1, Name = "Class 1" },
new Class { ClassId = 2, Name = "Class 2" }
}.AsQueryable();
public IQueryable<Class> GetClasses()
{
return _classes;
}
}
}