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");
    }

BlogEntryContentEntry 的派生词,ContentEntry.TagsICollection<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();
    }
}

我希望这已经足够清楚了。如果您有任何问题,请告诉我。