asp.net-mvc - 如何使用 Razor 在 ASP.NET MVC 4 中为具有 List 属性的对象创建编辑表单

标签 asp.net-mvc

我的 MVC 应用程序有一个使用 Razor 的编辑页面。

我有一个像这样的模型:

public class MyModelObject
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Description { get; set; }

    public List<MyOtherModelObject> OtherModelObjects { get; set; }
}

MyOtherModelObject 看起来像:

public class MyOtherModelObject
{
    public string Name { get; set; }

    public string Description { get; set; }
}

我正在为 MyModelObject 创建编辑页面。我需要一种方法来为 MyModelObject 的编辑页面上的表单添加空间,以便用户根据用户的意愿创建/添加任意数量的 MyOtherModelObject 实例到 OtherModelObjects 列表中。

我认为用户可以点击一个按钮,这将对另一个操作执行ajax,该操作返回表单元素的 PartialView (没有表单标签,因为这是我的编辑页面上表单的一部分)。当用户添加了他们想要的所有 MyOtherModelObjects 并填写了数据后,他们应该能够将编辑保存到现有的 MyModelObject,这将 HttpPost 到编辑操作,并希望所有 MyOtherModelObjects 都将位于正确的列表中。

我还需要用户在添加项目后能够重新排序。

有谁知道如何实现这个功能吗?是否已实现此解决方案的示例项目或在线示例演练?

最佳答案

这个blog post包含说明如何实现这一目标的分步指南。

<小时/>

更新:

根据评论部分的要求,我将逐步说明如何使上述文章适应您的场景。

型号:

public class MyOtherModelObject
{
    public string Name { get; set; }
    public string Description { get; set; }
}

public class MyModelObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public List<MyOtherModelObject> OtherModelObjects { get; set; }
}

Controller :

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyModelObject
        {
            Id = 1,
            Name = "the model",
            Description = "some desc",
            OtherModelObjects = new[]
            {
                new MyOtherModelObject { Name = "foo", Description = "foo desc" },
                new MyOtherModelObject { Name = "bar", Description = "bar desc" },
            }.ToList()
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyModelObject model)
    {
        return Content("Thank you for submitting the form");
    }

    public ActionResult BlankEditorRow()
    {
        return PartialView("EditorRow", new MyOtherModelObject());
    }
}

View (~/Views/Home/Index.cshtml):

@model MyModelObject

@using(Html.BeginForm())
{
    @Html.HiddenFor(x => x.Id)
    <div>
        @Html.LabelFor(x => x.Name)
        @Html.EditorFor(x => x.Name)
    </div>
    <div>
        @Html.LabelFor(x => x.Description)
        @Html.TextBoxFor(x => x.Description)
    </div>
    <hr/>
    <div id="editorRows">
        @foreach (var item in Model.OtherModelObjects)
        {
            @Html.Partial("EditorRow", item);
        }
    </div>
    @Html.ActionLink("Add another...", "BlankEditorRow", null, new { id = "addItem" })

    <input type="submit" value="Finished" />
}

部分(~/Views/Home/EditorRow.cshtml):

@model MyOtherModelObject

<div class="editorRow">
    @using (Html.BeginCollectionItem("OtherModelObjects"))
    {
        <div>
            @Html.LabelFor(x => x.Name)
            @Html.EditorFor(x => x.Name)
        </div>
        <div>
            @Html.LabelFor(x => x.Description)
            @Html.EditorFor(x => x.Description)
        </div>
        <a href="#" class="deleteRow">delete</a>
    }
</div>

脚本:

$('#addItem').click(function () {
    $.ajax({
        url: this.href,
        cache: false,
        success: function (html) {
            $('#editorRows').append(html);
        }
    });
    return false;
});

$('a.deleteRow').live('click', function () {
    $(this).parents('div.editorRow:first').remove();
    return false;
});
<小时/>

备注:BeginCollectionItem 自定义帮助器取 self 链接到的同一篇文章,但为了答案的完整性,我在此处提供它:

public static class HtmlPrefixScopeExtensions
{
    private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
    {
        var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
        string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();

        // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
        html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));

        return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
    }

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
        return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
    {
        // We need to use the same sequence of IDs following a server-side validation failure,  
        // otherwise the framework won't render the validation error messages next to each item.
        string key = idsToReuseKey + collectionName;
        var queue = (Queue<string>)httpContext.Items[key];
        if (queue == null)
        {
            httpContext.Items[key] = queue = new Queue<string>();
            var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
            if (!string.IsNullOrEmpty(previouslyUsedIds))
                foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
                    queue.Enqueue(previouslyUsedId);
        }
        return queue;
    }

    private class HtmlFieldPrefixScope : IDisposable
    {
        private readonly TemplateInfo templateInfo;
        private readonly string previousHtmlFieldPrefix;

        public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
        {
            this.templateInfo = templateInfo;

            previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
            templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
        }

        public void Dispose()
        {
            templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
        }
    }
}

关于asp.net-mvc - 如何使用 Razor 在 ASP.NET MVC 4 中为具有 List 属性的对象创建编辑表单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12377778/

相关文章:

c# - 使 ASP.NET MVC 路由 ID 参数成为必需的

c# - 是否可以将 2 个独立的 MVC 应用程序部署到一个 Azure Web 应用程序中?

c# - .net 中任务的说明

c# - MVC 中 javascript 中的嵌套 foreach

c# - 尝试单元测试时如何触发 Initialize 方法?

c# - AddIdentity 与 AddIdentityCore

javascript - 提前输入自动完成抛出错误

asp.net-mvc - ASP.NET MVC 中包含 "con"时出现奇怪的 404 错误

c# - Task.Run 在 ASP .NET MVC Web 应用程序中被认为是不好的做法吗?

c# - 为什么我们在 ninject 中使用 .ToSelf() 背后的主要逻辑是什么