0

Basically I have a form that I am dynamically adding objects to. I am doing this with AJAX so can just initialise the object and return it with JSON. Each new object has a unique GUID assigned to it so we can identify each object in the model collection when it is passed back into the action.

However, I need to support non JavaScript so am trying to write a solution that will post back the model and add or remove the given object from the model. There can be any number of these new objects on the model so I need to pass back several things to find out which object to delete before returning the model back to the view. This could be either

a) The GUID for the object the user has deleted.

b) The button that has been clicked to identify which object to delete.

The problem is that the partial view is generic and I would like to keep it that way so I'm trying to pass the identifying GUID back with the input button on each partial view but don't know how. I can easily do this with JavaScript because I just remove the created AJAX object from the page before posting it when the user clicks the remove link but can't figure out how to do it with a submit. Basically I want to do something like this:

@using (Project.Namespace.Infrastructure.Helpers.HtmlPrefixScopeExtensions.HtmlFieldPrefixScope _scope = Html.BeginCollectionItem())
{
<ul class="ulMedicationsControl">
    @Html.ActionLink("Remove This Object", "RemoveObject", null)

    @Html.Input("RemoveObject", "Remove This Object", new { Prefix = _scope.Prefix, objectGUID = IdentifyingGUID })

 @Html.HiddenFor(m => m.IdentifyingGUID);

    <li class="liQuestion">
        @Html.MandatoryLabelFor(m => m.myField)
        @Html.TextBoxFor(m => m.myField)
    </li>

</ul>

<div id="@(_scope.Prefix).ajaxPlaceholder"></div>
}

In the controller:

[ActionName("FormName")]
[AcceptParameter(Name = "RemoveObject", Value = "Remove This Object")]
public ActionResult RemoveObject(MyParentModel model, string Prefix, string objectGUID)
    {

        Guid ID = new Guid(objectGUID);

        foreach (ObjectModel object in model.objects){
            if (object.IdentifyingGUID  == ID)
            {
                model.objects.Remove(object);
                break;
            }
        }

        return View(model);
    }

Any help I would really appreciate as I simple can't figure out how to do this!

EDIT

Also just to add the prefix attribute simply identifies where in the form the object sits. This will be needed for me to find which object list to go through and remove the object from as there may be several lists in different placed in the model.

Ryan B
  • 3,364
  • 21
  • 35

1 Answers1

0

An HTML input only passes "name=value" when a form post occurs so that's all you have to work with. With <input type=submit> you're further limited by the fact that the button's value is its caption (i.e. "myControl=Click Me!" is posted), so you can't stick anything programmatically meaningful in the value.

Method 1: So you're left with encoding all the information you need into the input's name - an approach that works fine, but you'll have to have to go digging into the controller action method's FormCollection parameter rather than relying on model binding. For example:

<input name="delete$@(_scope.Prefix)$@objectGUID" type="submit" value="Delete me" />

Better, have a helper class that encapsulates the string format with a ToString override and has Parse/TryParse/etc static methods, which could be used like this:

<input name="@(new DeleteToken{Prefix=_scope.Prefix, objectGUID=IdentifyingGUID})"  type="submit" value="Delete me" />

In your action method:

[HttpPost]
public ActionResult Foo(FormCollection formData) 
{
    var deleteTokens = DeleteToken.ParseAll(formData.AllKeys);
    foreach (var token in deleteTokens)
    {
        //...do the deletion
    }
}

Method 2: An alternative approach is to group each item into its own <form> (bear in mind you can't nest forms) - so when the submit happens, only its surrounding form is posted in which you can stash hidden inputs with the necessary data. e.g.

<ul class="ulMedicationsControl">
    <form ... >
        <!-- hidden field and submit button and whatever else here -->
        ...
    </form>
</ul>
Duncan Smart
  • 31,172
  • 10
  • 68
  • 70
  • A sub form works for submitting the object I want to remove but the issue is I then can’t remove this from the model collection the list of objects resides in. Also the rest of the page then loses context i.e. anything else the user has entered in any other field outside of the form is lost when the sub form is submitted. – Kieron Wallace Oct 04 '11 at 07:55
  • Also to add this problem is different from this onehttp://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx – Kieron Wallace Oct 05 '11 at 15:32
  • Can only see that something approximating method 1 would work - encoding the data into the button's name. IIRC there are problems with some browsers (IE most likely) using ` – Duncan Smart Oct 06 '11 at 07:49
  • Method 1 gives the button a unique ID but you still can not identify what has submitted the request for deletion, therefore don't know which object to delete from the collection. As far as I can see this will just delete all matched objects from the collection (essentially all of them). I need to identify what has submitted the request so I can delete just that one object. The fall back plan for this is just to mark the object for deletion with a checkbox and then submit the remove action but this affects user experience. – Kieron Wallace Oct 06 '11 at 09:32
  • The key is: if you have multiple `` buttons in a form, *only the one you click* gets submitted. – Duncan Smart Oct 06 '11 at 10:56
  • To clarify further: even though I've put a `DeleteToken.ParseAll` there will only be one (unless you switched the control to ``). It just makes the dealing with the FormCollection easier. – Duncan Smart Oct 06 '11 at 14:43