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

前端 未结 2 1219
悲&欢浪女
悲&欢浪女 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<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" />
    }
    

    Partial (~/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>
    

    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("<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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-24 04:19

    I was able to take the lesson learned in this blog post http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ and apply it to my case where a ModelObject has several properties, many of which are List.

    I modified his scripts as appropriate to work with multiple lists within a model, and will blog my solution as soon as I can. It will definitely have to wait until after my current sprint. I'll post the link when I'm done.

    0 讨论(0)
提交回复
热议问题