0

As simple as I can explain this... I have a User object, which has Privilege(s). Privileges are of course another table using FK's to link them together.

In my User Edit page, I have an EditorFor for the privileges (which is working fine), which includes a remove button (also working fine)...

What I'm not sure about is how to add a new item to the table, and have that item be submitted with the rest of the privileges. I could just be burnt out from a long day of coding, so if I'm being dumb, just say so...

Anyways, some code:
Edit.cshtml

 <div class="form-group">
        @Html.LabelFor(model => model.Privileges, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            <table class="table-striped">
                <col style="width:40%; padding-right: 10px">
                <col style="width:10%">
                <col style="width:10%">
                <col style="width:10%">
                <thead>
                    <tr>
                        <td>Facility</td>
                        @foreach (var t in ViewBag.InitTypes)
                        {
                            <td>@t.Abbreviation</td>
                        }
                    </tr>
                </thead>
                @Html.EditorFor(m => m.Privileges)
            </table>

        </div>
    </div>

 function removeItem(item) {
    $('#priv_' + item).remove();
}

Privilege.cshtml (Template)

@model ENT_ComplianceInitiatives2_Web.Models.Privilege
@Html.HiddenFor(model => model.UserID)
@Html.HiddenFor(model => model.ID)
<tr id="priv_@Model.ID">
    <td>@Html.DropDownList("FacilityID")</td>
    <td>@Html.EditorFor(model => model.C)</td>
    <td>@Html.EditorFor(model => model.P)</td>
    <td>@Html.EditorFor(model => model.S)</td>
    <td><i class="fa fa-trash" onclick="removeItem(@Model.ID)"></i></td>
</tr>

And of course a screenshot showing how it comes out. SampleScreen

EDIT: Here's some code from my controller that I currently have in place.

for (int i = user.Privileges.Count - 1; i >=0; i--)
            {
                var p = user.Privileges.ElementAt(i);
                var priv = db.Privileges.Find(p.ID);
                if (priv == null)
                {
                    //ok, we need to add it
                    priv = db.Privileges.Create();
                    priv.C = p.C;
                    priv.P = p.P;
                    priv.S = p.S;
                    priv.UserID = user.ID;
                    db.Privileges.Add(priv);
                }
                else if (p.FacilityID == 0)
                {
                    //remove it - when removing the row from the table, the facilityID gets set to 0
                    db.Privileges.Remove(priv);
                }
                else
                {
                    //update it
                    priv.C = p.C;
                    priv.P = p.P;
                    priv.S = p.S;
                    db.Entry(priv).State = EntityState.Modified;  
                }

            }
Para
  • 836
  • 8
  • 18
  • do you want to add them using your controller or JQuery? – IndieTech Solutions May 18 '15 at 21:02
  • What I was thinking was add them to the table, then on formsubmit they would be included with the bound "Privileges" property, then I could check them in the controller and add if they don't exist. As for creating the new row, was attempting to do with jQuery – Para May 18 '15 at 21:08
  • Updated, added the controller code I'm currently using to handle the Privilege objects – Para May 18 '15 at 21:11
  • Firstly your claim that your 'remove button' works fine is incorrect. Clearly you have not tested this with multiple items. If you delete the first row from the table, your collection will be empty on postback because the `DefaultModelBinder` requires collection indexers to start at zero and be consecutive unless you include a special input for the collection index. –  May 19 '15 at 00:51
  • 1
    The easy way to handle dynamically adding and deleting items in a collection is to use the [BeginCollectionItem](https://www.nuget.org/packages/BeginCollectionItem/) helper (refer [example here](http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/)) although this means making an ajax call to add the new item. Alternatively a pure client side approach is shown in [this answer](http://stackoverflow.com/questions/29837547/set-class-validation-for-dynamic-textbox-in-a-table/29838689#29838689) –  May 19 '15 at 00:54
  • I'll take a look at the ajax approach tomorrow, nothing wrong with that. As for the delete button - I'm able to remove the first row and a middle row or two, just random middle rows, the last row and first row, or all rows without error. – Para May 19 '15 at 03:26
  • If your removing items using jquery/javascript then you obvioulsy not posting back to `IEnumerable`. What is your POST method? –  May 20 '15 at 12:17
  • I'm simply removing the row from the table with function removeItem(item) { $('#priv_' + item).remove(); } on submit / POST, that rows facilityID is now 0, and is handled in the code above. – Para May 20 '15 at 13:50

1 Answers1

0

Big thanks to @Stephen Muecke for pointing my down the right path. Here's how I got it working:

Edit.cshtml:

<div class="col-md-10">
            <table class="table-striped" id="privTable">
                <col style="width:40%; padding-right: 10px">
                <col style="width:10%">
                <col style="width:10%">
                <col style="width:10%">
                <thead>
                    <tr>
                        <td>Facility</td>
                        @foreach (var t in ViewBag.InitTypes)
                        {
                            <td>@t.Abbreviation</td>
                        }
                    </tr>
                </thead>
                <tbody id="privTableBody">
                    @Html.EditorFor(m => m.Privileges)
                </tbody>
                <tr>
                    <td>@Html.ActionLink("Add another...", "GetNewRow", null, new { id = "addItem" })</td>
                </tr>
            </table>

        </div>

Javascript to go with it:

function removeItem(item) {
    $.ajax({
        data: { rowID: item, userID: "@Model.ID" },
        url: "/Users/RemoveRow",
        cache: false,
        success: function (html) { $("#privTableBody").html(html); }
    });
    return false;
    //$('#priv_' + item).remove();
}

$("#addItem").click(function () {
    $.ajax({
        data: { userID: "@Model.ID" },
        url: this.href,
        cache: false,
        success: function (html) { $("#privTableBody").html(html); }
    });
    return false;
});

UsersController:

    [HttpGet]
    public ActionResult GetNewRow(int userID)
    {
        var r = db.Privileges.Create();
        r.UserID = userID;
        r.FacilityID = 1;
        db.Privileges.Add(r);
        db.SaveChanges();
        var result = PartialView("_Privileges",db.Users.Find(userID));
        return result;
    }

    [HttpGet]
    public ActionResult RemoveRow(int rowID, int userID)
    {
        var r = db.Privileges.Find(rowID);
        db.Privileges.Remove(r);
        db.SaveChanges();
        var result = PartialView("_Privileges", db.Users.Find(userID));
        return result;
    }

_Privileges partial view:

@model ENT_ComplianceInitiatives2_Web.Models.User
@Html.EditorFor(m => m.Privileges)

Privilege.cshtml (template)

@model ENT_ComplianceInitiatives2_Web.Models.Privilege

<tr id="priv_@Model.ID">
@Html.HiddenFor(model => model.UserID)
@Html.HiddenFor(model => model.ID)
<td>@Html.DropDownListFor(model => model.FacilityID, new SelectList(Model.FacilitiesList, "ID", "FacilityName", Model.FacilityID))</td>
<td>@Html.EditorFor(model => model.C)</td>
<td>@Html.EditorFor(model => model.P)</td>
<td>@Html.EditorFor(model => model.S)</td>
<td><i class="fa fa-trash" onclick="removeItem(@Model.ID)"></i></td>
</tr>
Para
  • 836
  • 8
  • 18
  • I guessing you have misunderstood my comments. Your solution means your generating the collection all over again and regenerating the table in the view every time you add or delete a row in the view - terrible performance! I suggest you read the links in my second comment to your question. –  May 21 '15 at 01:11