0

I'm currently using this Wizard in my application.

In one of my steps, I need to add and remove items from a table. Add functions works fine. But I can't make the Delete function work. I've searched SO and other resources online, can't find a solution.

This is what I've so far:

Wizard Step 2 Table:

  <table id="LibList" class="table table-responsive table-striped">
            <thead>
                <tr>
                    <th>
                        @Html.DisplayNameFor(model => model.Step3.Liabilities[0].Types)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Step3.Liabilities[0].Name)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Step3.Liabilities[0].Teams)
                    </th>
                    <th>
                        <label>Delete</label>
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in Model.Step3.Liabilities) {
                    Html.RenderPartial("_LibListItem", item);
                }
            </tbody>
        </table>

Partial View:

        <tr>
<td>
    @Html.DropDownListFor(model => model.Type, Model.Types, new { @class = "selectpicker form-control", id = string.Format("{0}_{1}", "TYPE", Model.ID) })
</td>
<td>
    @Html.TextBoxFor(model => model.Name, new { @class = "form-control", id = string.Format("{0}_{1}", "NAME", Model.ID) })
</td>
<td>
    @Html.TextBoxFor(model => model.Teams, new { @class = "form-control", @type = "currency", id = string.Format("{0}_{1}", "TERMS", Model.ID) })
</td>
<td>
    @Ajax.ActionLink("Delete", "Delete", "QuestionWizard", new { id = Model.ID },
    new AjaxOptions() {
        HttpMethod = "Delete",
        Confirm = "Are you sure you want to delete this record?",
        OnComplete = "function() { $(this).parent().parent().remove() }"
    },
    new { @class = "btn btn-primary" })
</td>

Add and Delete Action:

    public ActionResult AddRow() {
        LiabilityModel lib = new LiabilityModel();
        try {
            if (LiabilitySessionState.Count == 0) {
                lib.ID = 1;
            } else {
                lib.ID = LiabilitySessionState.LastOrDefault().ID + 1;
            }
            LiabilitySessionState.Add(lib);
            return PartialView("_LibListItem", lib);
        } catch (Exception ex) {
            System.Web.HttpContext.Current.Application.Add("LASTERROR", ex);
            return RedirectToAction("Index", "Error");
        }
    }

    public void Delete(int Id) {
        try {
            if (LiabilitySessionState.Count > 0) {
                LiabilitySessionState.RemoveAll(item => item.ID == Id);
            }
        } catch (Exception ex) {
            System.Web.HttpContext.Current.Application.Add("LASTERROR", ex);
            RedirectToAction("Index", "Error");
        }
    }

    public List<LiabilityModel> LiabilitySessionState {
        get {
            if (Session["LiabilityModel"] == null) {
                return LiabilitySessionState = new List<LiabilityModel>();
            } else {
                return Session["LiabilityModel"] as List<LiabilityModel>;
            }
        }
        set {
            Session["LiabilityModel"] = value;
        }
    }

Script:

<script type="text/javascript">
$(document).ready(function () {
    $('.add-button').click(function () {
        var action = "/QuestionWizard/AddRow";
        $.ajax({
            url: action,
            cache: false,
            success: function (result) {
                $("#LibList tbody").append($(result));
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            }
        });
    })
    $(".remove-button").click(function () {
        $(this).parents().parent().remove();
        return false;
    });
});

Right now, the Delete Action returns a blank page, because I'm not returning a view. But, I'm not sure what view to return.

Please help!

Thanks in advance.

capiono
  • 2,875
  • 10
  • 40
  • 76
  • http://stackoverflow.com/questions/25286797/how-to-handle-repeating-form-fields-in-asp-mvc/25287349#25287349 My answer here might help you – jamesSampica Feb 28 '15 at 20:43

1 Answers1

1

Firstly your Delete() method is changing data so it needs to be a POST, not a GET (which @Ajax.ActionLink() is). Next, your 'delete link' does not have class="remove-button" so $(".remove-button").click(function () { wont do anything. Since you already using jquery ajax methods to add items (refer notes below), there seems no point using the Ajax helpers anyway (your just adding extra overhead by loading the jquery.unobtrusive-ajax.js file).

Change the last <td> element of the partial to

<td>
  <button type="button" class="remove-button" data-id=@Model.ID>Delete</button>
</td>

Then in the main view, use the following script (note you need event delegation so you can remove dynamically added items)

var url = '@Url.Action("Delete", "YourControllerName")';
$('#LibList').on('click', .remove-button, function() {
  var row = $(this).closest('tr');
  var id = $(this).data('ID');
  if (id) { // assumes property ID is nullable, otherwise may need to test if id != 0
    $.post(url, {ID: id }, function(response) {
      if(response) {
        row.remove();
      } else {
        // Oops - display message?
      }
    });
  } else {
    // its a new row so it has not been saved yet - just delete the row
    row.remove();
  }
});

and the controller method should look like

[HttpPost]
public JsonResult Delete(int ID)
{
  // delete the item
  return Json(true); // or if an exception occurred, return(null) to indicate failure
}

Side notes:

  1. Both your AddRow() and Delete() methods include a return RedirectToAction() statement. You are making ajax calls which stay on the same page so RedirectToAction() is pointless (it will never be executed)
  2. If you believe the "Add functions works fine", then you must have some unnecessary hacks in order to make this work (your generating duplicate name attributes for the controls (without indexers) which will not bind to your collection of Liabilities). You need to generate existing items in a for loop and include a hidden input for an Index property, and use a client side template to generate new items; or you need to use the BeginCollectionItem helper if using a partial view. This answer shows how to use both techniques.
  3. You don't appear (and you should not need) to access the id attributes of the controls in your table. Instead of using id = string.Format("{0}_{1}", "NAME", Model.ID), just use id="" which will render the element without and id attribute.
Community
  • 1
  • 1
  • Thanks Stephen, that really helped. One more thing, I'm losing dropdown css, do you know why? – capiono Mar 01 '15 at 02:45
  • Not sure what you mean by _"losing dropdown css"_. Do you mean the styles you are expecting are not being applied to the ` –  Mar 01 '15 at 02:55