MVC - 将所有 ListBox 项传递回 Controller
MVC - pass all ListBox items back to Controller
我使用 Entity Framework 使用模型优先方法创建了一个数据库。除其他外,我有一个类型 ContentEntry
和一个类型 Tag
。每个 ContentEntry
可以有多个 Tag
,每个 Tag
可以被多个 ContentEntry
使用。它应该是这样的,没有标签在数据库中存在两次,因为那是 n:m 关系:
现在,我尝试创建一个 controller/view 来创建一个带有 Tag
的新 ContentEntry
。我不知道如何创建一个 ListBox 将 all 他的项目返回给控制器。 JavaScript(jQuery)对我来说没问题:
<span class="label">Tags</span>
@Html.ListBoxFor(model => model.Tags,
new MultiSelectList(Model.Tags),
new { id = "lbTags" })
<input type="text" id="tbTag" />
<input type="button" value="add" onclick="addTag();" />
<input type="button" value="delete" onclick="delTags();" />
<script>
function addTag() {
$('#lbTags').append($('<option>',
{ value: $("#tbTag").val(), text: $("#tbTag").val() }));
}
function delTags() {
$("#lbTags option:selected").remove();
}
</script>
@Html.ValidationMessageFor(model => model.Tags, "", new { @class = "input-error" })
但是我的 Tags
collection 总是空的:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBlogEntry
([Bind(Include = "Id,Published,Title,AccountId,HtmlContent,Tags")]BlogEntry blogEntry)
{
//blogEntry.Tags is always empty
if (ModelState.IsValid
{
db.ContentEntrySet.Add(blogEntry);
db.SaveChanges();
return RedirectToAction("Index");
}
BlogEntry
是 ContentEntry
的派生词,ContentEntry.Tags
是 ICollection<Tag>
.
有人知道如何解决这个任务吗?
编辑:
这是我的 GET-method 的 CreateBlogEntry:
public ActionResult CreateBlogEntry()
{
//ViewBag.Tags = db.TagSet;
return View(new BlogEntry()
{
Published = DateTime.Now,
});
}
默认模型绑定器会将多选中的选定值绑定到集合。
将您的观点更改为
@Html.ListBox("SelectedTags"
, new MultiSelectList(Model.Tags,"Name","Name")
, new { id = "lbTags" })
这样即使您从服务器加载值,它也能正常工作。请注意,我没有使用 ListBoxFor
,因为我想设置集合的名称。
进入 CreateBlogEntry
方法的 BlogEntry
模型将具有如下 属性
public IEnumerable<string> SelectedTags{ get; set; }
然后您可以使用此 SelectedTags
属性 并创建一个新模型,该模型将进入您的数据库。
如果您不想要此行为,则必须覆盖默认模型绑定器的行为。您可以找到有关活页夹的所有信息 in this MSDN post。我将尝试使用模型活页夹更新答案,但这足以同时解除对您的封锁。
另外请注意,您正在使用所有属性 BlogEntry
绑定是不必要的。你可以删除它。
更新 - 自定义模型活页夹版本
您可以像下面这样创建自己的活页夹:
public class FancyBinder:DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext
,ModelBindingContext bindingContext
,Type modelType)
{
if (modelType.Name == "BlogEntry")
{
return BindBlogEntry(controllerContext, bindingContext, modelType);
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
private static object BindBlogEntry(ControllerContext controllerContext
,ModelBindingContext bindingContext
,Type modelType)
{
var tagsOnForm = controllerContext.HttpContext.Request.Form["Tags"];
return new BlogEntry
{
Content = controllerContext.HttpContext.Request.Form["Content"],
Tags = GetTags(tagsOnForm)
};
}
private static List<Tag> GetTags(string tagsOnForm)
{
var tags = new List<Tag>();
if (tagsOnForm == null)
return tags;
tagsOnForm.Split(',').ForEach(t=>tags.Add(new Tag {Name = t}));
return tags;
}
}
您可以在 global.asax 中连接此活页夹,如下所示:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ModelBinders.Binders.DefaultBinder = new FancyBinder();
}
}
我希望这已经足够清楚了。如果您有任何问题,请告诉我。
我使用 Entity Framework 使用模型优先方法创建了一个数据库。除其他外,我有一个类型 ContentEntry
和一个类型 Tag
。每个 ContentEntry
可以有多个 Tag
,每个 Tag
可以被多个 ContentEntry
使用。它应该是这样的,没有标签在数据库中存在两次,因为那是 n:m 关系:
现在,我尝试创建一个 controller/view 来创建一个带有 Tag
的新 ContentEntry
。我不知道如何创建一个 ListBox 将 all 他的项目返回给控制器。 JavaScript(jQuery)对我来说没问题:
<span class="label">Tags</span>
@Html.ListBoxFor(model => model.Tags,
new MultiSelectList(Model.Tags),
new { id = "lbTags" })
<input type="text" id="tbTag" />
<input type="button" value="add" onclick="addTag();" />
<input type="button" value="delete" onclick="delTags();" />
<script>
function addTag() {
$('#lbTags').append($('<option>',
{ value: $("#tbTag").val(), text: $("#tbTag").val() }));
}
function delTags() {
$("#lbTags option:selected").remove();
}
</script>
@Html.ValidationMessageFor(model => model.Tags, "", new { @class = "input-error" })
但是我的 Tags
collection 总是空的:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBlogEntry
([Bind(Include = "Id,Published,Title,AccountId,HtmlContent,Tags")]BlogEntry blogEntry)
{
//blogEntry.Tags is always empty
if (ModelState.IsValid
{
db.ContentEntrySet.Add(blogEntry);
db.SaveChanges();
return RedirectToAction("Index");
}
BlogEntry
是 ContentEntry
的派生词,ContentEntry.Tags
是 ICollection<Tag>
.
有人知道如何解决这个任务吗?
编辑: 这是我的 GET-method 的 CreateBlogEntry:
public ActionResult CreateBlogEntry()
{
//ViewBag.Tags = db.TagSet;
return View(new BlogEntry()
{
Published = DateTime.Now,
});
}
默认模型绑定器会将多选中的选定值绑定到集合。
将您的观点更改为
@Html.ListBox("SelectedTags"
, new MultiSelectList(Model.Tags,"Name","Name")
, new { id = "lbTags" })
这样即使您从服务器加载值,它也能正常工作。请注意,我没有使用 ListBoxFor
,因为我想设置集合的名称。
进入 CreateBlogEntry
方法的 BlogEntry
模型将具有如下 属性
public IEnumerable<string> SelectedTags{ get; set; }
然后您可以使用此 SelectedTags
属性 并创建一个新模型,该模型将进入您的数据库。
如果您不想要此行为,则必须覆盖默认模型绑定器的行为。您可以找到有关活页夹的所有信息 in this MSDN post。我将尝试使用模型活页夹更新答案,但这足以同时解除对您的封锁。
另外请注意,您正在使用所有属性 BlogEntry
绑定是不必要的。你可以删除它。
更新 - 自定义模型活页夹版本
您可以像下面这样创建自己的活页夹:
public class FancyBinder:DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext
,ModelBindingContext bindingContext
,Type modelType)
{
if (modelType.Name == "BlogEntry")
{
return BindBlogEntry(controllerContext, bindingContext, modelType);
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
private static object BindBlogEntry(ControllerContext controllerContext
,ModelBindingContext bindingContext
,Type modelType)
{
var tagsOnForm = controllerContext.HttpContext.Request.Form["Tags"];
return new BlogEntry
{
Content = controllerContext.HttpContext.Request.Form["Content"],
Tags = GetTags(tagsOnForm)
};
}
private static List<Tag> GetTags(string tagsOnForm)
{
var tags = new List<Tag>();
if (tagsOnForm == null)
return tags;
tagsOnForm.Split(',').ForEach(t=>tags.Add(new Tag {Name = t}));
return tags;
}
}
您可以在 global.asax 中连接此活页夹,如下所示:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ModelBinders.Binders.DefaultBinder = new FancyBinder();
}
}
我希望这已经足够清楚了。如果您有任何问题,请告诉我。