Nested BeginCollectionItem

前端 未结 3 1113
滥情空心
滥情空心 2020-12-16 16:39

I\'m using Steve Sanderson\'s BeginCollectionItem approach to add dynamic content. Everything works fine when I\'m doing it on the first level. However, when try to implemen

3条回答
  •  不知归路
    2020-12-16 17:06

    I couldn't properly adapt Job Stevens' method with MVC 5. I just use Job Stevens' below extension class with name BeginCollectionItem2

    public static class HtmlPrefixScopeExtensions
    {
        private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
    
        public static IDisposable BeginCollectionItem2(this HtmlHelper html, string collectionName)
        {
            if (html.ViewData["ContainerPrefix"] != null)
            {
                collectionName = string.Concat(html.ViewData["ContainerPrefix"], ".", collectionName);
            }
    
            var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
            string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
    
            var htmlFieldPrefix = string.Format("{0}[{1}]", collectionName, itemIndex);
    
            html.ViewData["ContainerPrefix"] = htmlFieldPrefix;
    
            // 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, htmlFieldPrefix);
        }
    
        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;
            }
        }
    }
    

    scripts like below:

    function addRow() {
    
            $.ajax({
                type: "POST",
                data: {processTypeId:@Model.Id},
                url: '@Url.Action("GetFlowItemRow", "Flow")',
                success: function (partialView) {
                    $('#divItemList').append(partialView);
                }
            });
        }
    
     function addParameterRow(rw, prx) {
    
            $.ajax({
                type: "POST",
                url: '@Url.Action("GetFlowItemParameterRow", "Flow")' + '?pId=' + '@Model.Id' + '&prefix=' + prx ,
                success: function (partialView) {
                    rw.closest('table').find("tbody").append(partialView);
                }
            });
        }
    

    html buttons to add partial views like :

    
     
    
    
     
    
    

    partial view methods on controller:

    public PartialViewResult GetFlowItemRow(int? processTypeId)
        {
            FlowItemModel _item = new FlowItemModel() { ProcessTypeId = processTypeId ?? 0 };
            return PartialView("~/Views/Flow/Partial/_FlowItem.cshtml", _item);
        }
    
        public PartialViewResult GetFlowItemParameterRow(int? pId, string prefix)
        {
            ViewData["ContainerPrefix"] = prefix;
            FlowItemParameterModel _item = new FlowItemParameterModel() { };
    
            return PartialView("~/Views/Flow/Partial/_FlowItemParameter.cshtml", _item);
        }
    

    Flow Item partial :

    
            @using (Html.BeginCollectionItem2("OperationList"))
            {
                @Html.HiddenFor(model => model.ItemId)
                
                    @Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
                
                
                    
    Name Unit Add
    }

    Item Parameter Partial

    
        @using (Html.BeginCollectionItem2("ParameterList"))
        {
    
            @Html.TextBoxFor(m => m.ParameterName, new { @class = "form-control" })
            
                @Html.TextBoxFor(m => m.Unit, new { @class = "form-control" })
                                                            
        }
    

提交回复
热议问题