1
\$\begingroup\$

Here is my working attempt to allow multiple/infinite child record entries with the original parent post in .NET MVC. Any refactoring to simplify my code would be greatly appreciated:

Controller:

public ActionResult Create()
{
    EquipmentLicense equipmentlicense = new EquipmentLicense();
    equipmentlicense.EquipmentLicenseKeys = new List<mySolution.Models.EquipmentLicenseKey>() { new EquipmentLicenseKey() };

    return View(equipmentlicense);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "EquipmentLicenseId,ProductName")] EquipmentLicense EquipmentLicense, FormCollection formCollection)
{
    try
    {
        if (ModelState.IsValid)
        {
            EquipmentLicense.EquipmentLicenseId = Guid.NewGuid();
            db.EquipmentLicenses.Add(EquipmentLicense);

            foreach (var formcollectionkey in formCollection.AllKeys)
            {
                int intCollectionLength = formcollectionkey.Length;

                if (intCollectionLength == 68)
                {
                    if (formcollectionkey.Substring(58).ToString() == "LicenseKey")
                    {
                        if (formCollection[formcollectionkey].Trim().Length > 0)
                        {
                            EquipmentLicenseKey newEquipmentLicenseKey = new EquipmentLicenseKey();

                            string stringLicenseKey = formCollection[formcollectionkey];
                            string guidEquipmentLicenseKeyId = formcollectionkey;
                            guidEquipmentLicenseKeyId = guidEquipmentLicenseKeyId.Remove(guidEquipmentLicenseKeyId.Length - 12, 12);
                            guidEquipmentLicenseKeyId = guidEquipmentLicenseKeyId.Remove(0, 20);

                            newEquipmentLicenseKey.EquipmentLicenseKeyId = Guid.Parse(guidEquipmentLicenseKeyId);
                            newEquipmentLicenseKey.EquipmentLicenseId = EquipmentLicense.EquipmentLicenseId;
                            newEquipmentLicenseKey.LicenseKey = stringLicenseKey;

                            db.EquipmentLicenseKeys.Add(newEquipmentLicenseKey);
                        }
                    }
                }
            }

            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    catch
    {
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
    }

    return View(EquipmentLicense);
}

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult LicenseKeyEntryRow()
{
    return PartialView("LicenseKeyEntryEditor");
}

Infrastructure:

public static IDisposable BeginCollectionItem<TModel>(this HtmlHelper<TModel> html, string collectionName)
{
    if (String.IsNullOrEmpty(collectionName))
    {
        throw new ArgumentException("collectionName is null or empty.", "collectionName");
    }

    string strGuid = Guid.NewGuid().ToString();
    string collectionIndexFieldName = String.Format("{0}.Index", collectionName);
    string itemIndex = null;

    if (html.ViewData.ContainsKey(JQueryTemplatingEnabledKey))
    {
        itemIndex = "${index}";
    }
    else
    {
        itemIndex = GetCollectionItemIndex(collectionIndexFieldName, strGuid);
    }

    string collectionItemName = String.Format("{0}[{1}]", collectionName, itemIndex);
    TagBuilder indexField = new TagBuilder("input");
    indexField.MergeAttributes(new Dictionary<string, string>() {
        { "name", collectionIndexFieldName },
        { "value", itemIndex },
        { "type", "hidden" },
        { "autocomplete", "off" }
    });

    html.ViewContext.Writer.WriteLine(indexField.ToString(TagRenderMode.SelfClosing));

    return new CollectionItemNamePrefixScope(html.ViewData.TemplateInfo, collectionItemName);
}

private const string JQueryTemplatingEnabledKey = "__BeginCollectionItem_jQuery";

public static MvcHtmlString CollectionItemJQueryTemplate<TModel, TCollectionItem>(this HtmlHelper<TModel> html, string partialViewName, TCollectionItem modelDefaultValues)
{
    ViewDataDictionary<TCollectionItem> viewData = new ViewDataDictionary<TCollectionItem>(modelDefaultValues);
    viewData.Add(JQueryTemplatingEnabledKey, true);

    return html.Partial(partialViewName, modelDefaultValues, viewData);
}

private static string GetCollectionItemIndex(string collectionIndexFieldName, string strGuid)
{
    Queue<string> previousIndices = (Queue<string>)HttpContext.Current.Items[collectionIndexFieldName];

    if (previousIndices == null)
    {
        HttpContext.Current.Items[collectionIndexFieldName] = previousIndices = new Queue<string>();
        string previousIndicesValues = HttpContext.Current.Request[collectionIndexFieldName];

        if (!String.IsNullOrWhiteSpace(previousIndicesValues))
        {
            foreach (string index in previousIndicesValues.Split(','))
            {
                previousIndices.Enqueue(index);
            }
        }
    }

    return previousIndices.Count > 0 ? previousIndices.Dequeue() : strGuid;
}

private class CollectionItemNamePrefixScope : IDisposable
{
    private readonly TemplateInfo _templateInfo;
    private readonly string _previousPrefix;

    public CollectionItemNamePrefixScope(TemplateInfo templateInfo, string collectionItemName)
    {
        this._templateInfo = templateInfo;
        _previousPrefix = templateInfo.HtmlFieldPrefix;
        templateInfo.HtmlFieldPrefix = collectionItemName;
    }

    public void Dispose()
    {
        _templateInfo.HtmlFieldPrefix = _previousPrefix;
    }
}

Create.cshtml:

@model mySolution.Models.EquipmentLicense

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <div class="form-horizontal">
        <div class="row">
            <div class="col-md-4">
                <div class="form-group">
                    @Html.LabelFor(model => model.ProductName, htmlAttributes: new { @class = "control-label " })
                    <div class="">
                        @Html.EditorFor(model => model.ProductName, new { htmlAttributes = new { @class = "form-control", width = "100&", style = "max-width:100&" } })
                        @Html.ValidationMessageFor(model => model.ProductName, "", new { @class = "text-danger" })
                    </div>
                </div>
            </div>
        </div>

        <div class="col-md-12">
            <div class="form-group">
                @Html.LabelFor(model => model.EquipmentLicenseKeys.FirstOrDefault().LicenseKey, htmlAttributes: new { @class = "control-label " })
                <div class="row">
                    <div class="col-md-1">
                        <a id="addLicenseKey" href="#" class="btn btn-primary btn-md">Add <span class="glyphicon glyphicon-plus"></span></a>
                    </div>
                    <div class="col-md-11">

                            <ul id="LicenseKeyEditor" style="list-style-type: none">
                                @if (Model.EquipmentLicenseKeys != null)
                                {
                                    foreach (EquipmentLicenseKey equipmentlicensekey in Model.EquipmentLicenseKeys)
                                    {
                                        Html.RenderPartial("LicenseKeyEntryEditor", equipmentlicensekey);
                                    }
                                }
                            </ul>
                    </div>
                </div>
            </div>
        </div>

        <hr />
        <div class="row">
            <div class="form-group" >
                <div class="col-md-1 col-md-offset-4">
                    <a href="@Url.Action("Index", "License")" class="btn btn-default pull-left">Cancel&nbsp;&nbsp;<span class="glyphicon glyphicon-remove"></span></a>
                </div>
                <div class="col-md-1 col-md-offset-2">
                    <button type="submit" value="Create License" class="btn btn-primary pull-right">Submit&nbsp;&nbsp;<span class="glyphicon glyphicon-floppy-disk"></span></button>
                </div>
            </div>
        </div>
    </div>
}
@section Scripts {
    @Styles.Render("~/Content/css")
    @Styles.Render("~/Content/jquery-theme")
    @Scripts.Render("~/bundles/jquery")
    @Styles.Render("~/Content/bootstrap")
    @Scripts.Render("~/bundles/bootstrap")
    <script type="text/javascript" language="javascript">
        $(document).ready(function () {
            $("#LicenseKeyEditor").sortable();
            $("#addLicenseKey").click(function () {
                $.get('/License/LicenseKeyEntryRow', function (template)
                {
                    $("#LicenseKeyEditor").append(template);
                });
            });
        })
    </script>
}

LicenseKeyEntryEditor.cshtml:

@model mySolution.Models.EquipmentLicenseKey
@using mySolution.Infrastructure;

<li>
    @using (Html.BeginCollectionItem("EquipmentLicenseKey"))
    {
        <div class="row">
            <div class="col-md-11">
                <div class="form-group">
                    <div class="">
                        @Html.EditorFor(m => m.LicenseKey, new { htmlAttributes = new { @class = "form-control", width = "100&", style = "max-width:100&" } })
                        @Html.ValidationMessageFor(model => model.LicenseKey, "", new { @class = "text-danger" })
                    </div>
                </div>
            </div>
            <div class="col-md-1">
                <a href="#" class="btn btn-primary btm-sm" onclick="$(this).parent().parent().remove();">
                    Delete <span class="glyphicon glyphicon-trash"></span>
                </a>
            </div>
        </div>
    }

</li>
\$\endgroup\$
  • 1
    \$\begingroup\$ Please avoid using <code> tags along with &lt; and &gt; when posting code here. Use indentation only. \$\endgroup\$ – Jamal Feb 12 '17 at 22:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.