How do I make an Edit Form for an object with a List property in ASP.NET MVC 4 with Razor

前端 未结 2 1232
悲&欢浪女
悲&欢浪女 2020-12-24 03:29

I have an Edit page for my MVC application, using Razor.

I have a Model like:

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

    publi         


        
2条回答
  •  自闭症患者
    2020-12-24 04:13

    This blog post contains a step by step guide illustrating how to achieve that.


    UPDATE:

    As requested in the comments section I am illustrating step by step how to adapt the aforementioned article to your scenario.

    Model:

    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 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)
        
    @Html.LabelFor(x => x.Name) @Html.EditorFor(x => x.Name)
    @Html.LabelFor(x => x.Description) @Html.TextBoxFor(x => x.Description)

    @foreach (var item in Model.OtherModelObjects) { @Html.Partial("EditorRow", item); }
    @Html.ActionLink("Add another...", "BlankEditorRow", null, new { id = "addItem" }) }

    Partial (~/Views/Home/EditorRow.cshtml):

    @model MyOtherModelObject
    
    
    @using (Html.BeginCollectionItem("OtherModelObjects")) {
    @Html.LabelFor(x => x.Name) @Html.EditorFor(x => x.Name)
    @Html.LabelFor(x => x.Description) @Html.EditorFor(x => x.Description)
    delete }

    Script:

    $('#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;
    });
    

    Remark: the BeginCollectionItem custom helper is taken from the same article I've linked to, but I am providing it here for completeness sake of the answer:

    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("", 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 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)httpContext.Items[key];
            if (queue == null)
            {
                httpContext.Items[key] = queue = new Queue();
                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;
            }
        }
    }
    

提交回复
热议问题