Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
170 views
in Technique[技术] by (71.8m points)

c# - BeginCollectionItem partial within partial not behaving correctly

I am trying to bind all my model data at once through a form submission in MVC 5 using an edited version of BeginCollectionItem as discussed in Joe Steven's blog here.

The model, Company, has a List<Pa_Ipv4>, the class Pa_Ipv4 in turn has a List<IpAllocation>, I want to access and save to the database all the properties of the IpAllocation in each Pa_Ipv4.

IE: Model.pa_ipv4s[x].requestedIps[x].subnet

The main page is using model Company, which has a partial accepting Pa_Ipv4, which has a partial accepting IpAllocation.

Question 1: In my controller, I'm setting a string property for the first item in the list (requestedIp), but when I submit and postback, the property (allocationType) is null, this property needs to be hard coded as it's for internal use within the DB - why is this being reset?

Reason: The property isn't in the post method, as such what is initially declared is discarded as it's not within the end post.

Possible Solution: Use a hidden property within the form so that it is present when the form is posted and the user cannot access the property.

Question 2: BeginCollectionItem is naming attributes appropriately, IE: pa_ipv4s[8e075d50-a5fb-436f-9cef-85abfb6910e3].requestedIps[b693b83c-b6b1-4c42-b983-4d058e766d4c].subnet, but only the initial model, it's then ignoring any others created, what have I done wrong?

Reason: The GUID needed for a prefix generated by the Pa_Ipv4 sections BeginCollectionItem is not able to be accessed by the IpAllocation BeginCollectionItem, as such only the initial content has the correct prefixes, anything added hereafter misses the necessary prefix.

Another potential solution is essentially the same concept, but instead of using a div, use html data attribute instead so that it's accessible.

I think both of the issues I'm experiencing are to do with how I've set my controller up, but I've included the Views and Model below as well. The model contains all the properties, lots of these have been removed in my views to save space as these are not causing the issue.

Create

@model Company

@{
    Layout = "~/Views/Shared/_Layout.cshtml";   
}
@using (Html.BeginForm())
{
    <div class="jumboservice">
        <div data-role="page">
            <div data-role="header">
                <h2>PA IPv4 Request Form</h2>
            </div>
            <div class="ui-content" data-role="main">
                <h3>Company Details</h3>
                <div class="ui-grid-c ui-responsive">
                    <div class="ui-block-a">
                        <p class="lblStyle">Company Name</p>
                        <span>
                            @Html.EditorFor(m => m.name)
                            @Html.ValidationMessageFor(m => m.name)
                        </span>
                    </div>
                </div>
            </div>
            <br />
                @foreach (var i in Model.pa_ipv4s)
                {
                    @Html.Partial("Pa_IPv4View", i)
                }
            <br />
            <div data-role="main" class="ui-content">
                <div data-role="controlgroup" data-type="horizontal">
                    <input type="submit" class="ui-btn" value="Create" />
                </div>
            </div>
        </div>
    </div>
}
<script type="text/javascript">
    $(function () {
            $('#addItemRIpM').on('click', function () {
                $.ajax({
                    url: '@Url.Action("RequestedManager")',
                    cache: false,
                    success: function (html) { $("#editorRowsRIpM").append(html); }
                });
                return false;
            });
            $('#editorRowsRIpM').on('click', '.deleteRow', function () {
                $(this).closest('.editorRow').remove();
            });
        });
</script>

Pa_Ipv4 Partial

@model Pa_Ipv4

    @using (HtmlHelpers.BeginCollectionItem.HtmlPrefixScopeExtensions.BeginCollectionItem(Html,"pa_ipv4s"))
{
    @Html.AntiForgeryToken()
    <div class="ui-grid-c ui-responsive">
        <div class="ui-block-a">
            <p class="lblStyle">Subnet</p>
        </div>
        <div class="ui-block-b">
            <p class="lblStyle">Size(CIDR)</p>
        </div>
        <div class="ui-block-c">
            <p class="lblStyle">Mask</p>
        </div>
        <div class="ui-block-d">
        </div>
    </div>
    @*Request IP Address Space List*@
    <div id="editorRowsRIpM">
        @foreach (var item in Model.requestedIps)
        {
            @Html.Partial("RequestedIpView", item)
        }
    </div>
    @Html.ActionLink("Add", "RequestedManager", null, new { id = "addItemRIpM", @class = "button" })
}

RequestedIp Partial

@model IpAllocation

<div class="editorRow">
    @using (HtmlHelpers.BeginCollectionItem.HtmlPrefixScopeExtensions.BeginCollectionItem(Html, "requestedIps"))
    {
        <div class="ui-grid-c ui-responsive">
            <div class="ui-block-a">
                <span>
                    @Html.TextBoxFor(m => m.subnet)
                </span>
            </div>
            <div class="ui-block-b">
                <span>
                    @Html.TextBoxFor(m => m.cidr)
                </span>
            </div>
            <div class="ui-block-c">
                <span>
                    @Html.TextBoxFor(m => m.mask)
                    <span class="dltBtn">
                        <a href="#" class="deleteRow">Remove</a>
                    </span>
                </span>
            </div>
        </div>
    }
</div>

Controller

public ActionResult Create()
        {
            var cmp = new Company();
            cmp.contacts = new List<Contact>
            {
                new Contact { email = "", name = "", telephone = "" }
            };
            cmp.pa_ipv4s = new List<Pa_Ipv4>
            {
                new Pa_Ipv4
                { 
                    ipType = "Pa_IPv4", registedAddress = false, existingNotes = "",
                    numberOfAddresses = 0, returnedAddressSpace = false, additionalInformation = "",
                    requestedIps = new List<IpAllocation>
                    {
                        new IpAllocation { allocationType = "Requested", cidr = "", mask = "", subnet  = "" } // allocationType is null in cmp in the Create[HttpPost]
                    }
                }
            };

            return View(cmp);
        }

        public ActionResult Pa_IPv4Manager()
        {
            return PartialView("Pa_IPv4View", new Pa_Ipv4());
        }
        public ActionResult RequestedManager()
        {
            return PartialView("RequestedIpView", new IpAllocation { allocationType = "Requested" }); // allocationType is null in cmp in the Create[HttpPost]
        }

        // POST: Pa_Ipv4/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Company cmp) //only one requestedIps count regardless of how many add
        {
            if (ModelState.IsValid)
            {
                db.companys.Add(cmp);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

Model

[Table("Ipv_Base")]
public class Ipv_Base
{
    [Key]
    public int ipv_baseId { get; set; }
    public int companyId { get; set; }
    [ForeignKey("companyId")]
    public Company company { get; set; }
    public string ipType { get; set; }
    [Required]
    public bool registedAddress { get; set; }
    [Required]
    [DataType(DataType.MultilineText)]
    public string existingNotes { get; set; }
    [Required]
    public int numberOfAddresses { get; set; }
    [Required]
    public bool returnedAddressSpace { get; set; }
    [DataType(DataType.MultilineText)]
    public string additionalInformation { get; set; }
    // navigation properties
    public virtual IList<IpAllocation> requestedIps { get; set; }
}
[Table("Company")]
public class Company
{
    [Key]
    public int companyId { get; set; }
    [Required]
    public string name { get; set; }
    [Required]
    public string telephone { get; set; }
    [Required]
    public string regNumber { get; set; }
    // navigation properties to keep track of the models that belong to the company
    public virtual IList<Pa_Ipv4> pa_ipv4s { get; set; }

}
[Table("IpAllocation")]
public class IpAllocation
{
    [Key]
    public int ipAllocationId { get; set; }
    public int ipv_BaseId { get; set; }
    [ForeignKey("ipv_BaseId")]
    public Ipv_Base ipv_Base { get; set; }
    [Required]
    public string allocationType { get; set; }
    [Required]
    public string subnet { get; set; }
    [Required]
    public string cidr { get; set; }
    [Required]
    public string mask { get; set; }
}
public class Pa_Ipv4 : Ipv_Base
{
    public Pa_Ipv4()
    {
        ipType = "pa_ipv4";
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Question 1 Solution:

The issue with Q1 was that the property value I was assigning in the controller wasn't being parsed back from the form post, because the property wasn't there.

Added a hidden field for the property to rectify the pesky null.

<div class="ui-block-a">
                <span>
                    @Html.HiddenFor(m => m.allocationType)
                    @Html.TextBoxFor(m => m.subnet, new { @class = "checkFiller" })
                </span>
            </div>

Question 2 Solution:

The issues that I was facing with the GUID of the first model being attached as the prefix to the second model was largely due to how I was sending data using AJAX to the controller action method.

The code snippets shown below fix the issues and display the correctly bound GUIDs.

name="pa_ipv4s[f7d8d024-5bb6-451d-87e3-fd3e3b8c1bba].requestedIps[d5c08a43-f65e-46d1-b224-148225599edc].subnet" is now being shown on the dynamically created model properties, not just the initially created.

When running in debug in visual studio and hovering over the model, digging down into the data shows the correct counts of the model lists.

Controller ActionMethod:

public ActionResult ExistingManager(string containerPrefix)
        {
            ViewData["ContainerPrefix"] = containerPrefix;
            return PartialView("ExistingIpView", new IpAllocation { allocationType = "Existing" });
        }

AJAX GET Method calling Controller ActionMethod:

$('#addItemEIpM').on('click', function () {
            $.ajax({
                url: '@Url.Action("ExistingManager")',
                cache: false,
                data: 'containerPrefix=' + $('#addItemEIpM').data('containerprefix'),
                success: function (html) {
                    $("#editorRowsEIpM").append(html);
                }
            });
            return false;
        });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...