-1

In my MVC application I have a view where I will display different data from a SQL table. This will generate different amount of drop down lists and text boxes, depending on what is passed in from the Model.

My issue is if I want to then use that data I can't seem to figure out how I can relate control X to object Y in SQL. For example, if I have 2 textboxes that I want to do an update on, then when the Post happens in my application the FormCollection parameter will let me see the Value of the objects, but not their control name or any form of identifying factor.

I could set the Value to a combination of the entered value + a name, then split this, but it seems very much like a lazy workaround.

I've tried to assign an ID to each, for example:

    @foreach (DataObject item in Model.AllDataObjects)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Data)
            </td>
            <td>
                @if (item.Rule.Contains("Yes;No"))
                {
                    @Html.DropDownListFor(model => item.Value, new List<SelectListItem>
                        {
                            new SelectListItem {Text="Yes", Value="Yes"},
                            new SelectListItem {Text="No", Value="No" }
                            }, new { @id = item.ObjectId });
                }
                else
                {
                    @Html.TextAreaFor(model => item.Value, new { style = "width: 400px;", @rows = 5, @id = item.ObjectId  })
                }
            </td>
        </tr>
    }

Edit: The following is my Post ActionResult method in the Controller, albeit it isn't complete as I can't figure out how to get an ID for the control from the FormCollection

    [HttpPost]
    [ValidateInput(false)]
    public ActionResult UpdateData(FormCollection collection, int objectId=0)
    {
        try
        {
                int propertyTypeId = 0;
                string propertyValue = string.Empty;

            // Get all the control values from the collection
            string[] allValues = new string[] { };
            IValueProvider valueProvider = collection.ToValueProvider();
            foreach(string key in collection.Keys)
            {
                ValueProviderResult result = valueProvider.GetValue(key);

                allValues = result.RawValue as string[];

            }

                ObjectData objectData = _execution.GetObjectDetails(0);

                UpdateDataResponse result = _execution.UpdateData(0, objectId,
                    objectValue, UserName);

                return RedirectToAction("Details",
                    new { ObjectId = objectData.ObjectId, error = result.ErrorMessage });
        }
        catch (Exception ex)
        {
          // My exception handling here
        }
    }

So I can see in the mark-up that the controls are assigned the object ID as their own ID, but how can I get this back? When I check FormCollection I only see the values for each control, but no way of identifying which is which.

Edit: I'm using MVC version 4.

MattR
  • 641
  • 3
  • 17
  • 38
  • MVC versión? Can you show the Action method code? – Balde Mar 17 '16 at 16:35
  • I've updated the original post with your suggestions – MattR Mar 17 '16 at 16:40
  • 3
    Add a HiddenField to put the ID... so it is posted. – Romias Mar 17 '16 at 17:09
  • Is there any other way to post the info back? That will mean dynamically creating hidden fields on top of everything else – MattR Mar 17 '16 at 17:25
  • 1
    @MattR, btw you can store any static content inside of one hidden fields value. Just convert it to some JSON string and then put that string to hidden field. Another approach - have array of input fields which will be rendered via some html helper function - but I think it's overhead. BTW you can always get JSON string from hidden field on client side, make an object from that string, work with object fields and convert to JSON string back and store to hidden field. I think it's flexible approach. – Anton Norko Mar 17 '16 at 17:37
  • Never use `FormCollection` in MVC. You view should be `for(int i = 0; i < Model.AllDataObjects.Count; i++) { @Html.HiddenFor(m => m.AllDataObjects[i].objectId) ...... @Html.TextAreaFor(m => m..AllDataObjects[i].Value) .... }` and the POST method should be `public ActionResult UpdateData(yourModel model)` where `yourModel` is the same type as you used in the view. The model will all be bound for you. –  Mar 18 '16 at 02:44
  • And refer also [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) for an explanation of why you cant use a `foreach` loop in a view. –  Mar 18 '16 at 02:45
  • @StephenMuecke I've modified my code to us a for loop and have the model as a parameter in the ActionResult, although that just returns the value of the text box / drop down list. I will have to keep tinkering. – MattR Mar 18 '16 at 11:17
  • A form will only post the values of its successful form controls, so if you want the value of each `objectId`, then you need to include an input for it - `@Html.HiddenFor(m => m.AllDataObjects[i].objectId)` –  Mar 18 '16 at 11:20
  • Seems I have no choice but to use a hidden field, I've now implemented that. On the topic of not using FormCollection, does the following URL cover the bulk of the reasoning to not use it? Or are there other considerations? Are there cases to use it? http://stackoverflow.com/questions/17002022/is-there-any-good-reason-to-use-formcollection-instead-of-viewmodel – MattR Mar 18 '16 at 15:31
  • That answer covers the key points (although there are many others - e.g. taking into account values posted as route parameters or json etc). The source code for the `DefaultModelBinder` and associated `ValueProvider`, `ValueProviderFactory`. `ModelMetatadata` etc alone is dozens of pages of code. I have never seen a valid case for using `FormCollection` - its just throwing away all the in-built features of MVC. –  Mar 18 '16 at 22:05
  • @StephenMuecke your comments have helped me greatly, could you please post them up as an answer? Then I can accept it as the solution to my queries. – MattR Mar 21 '16 at 09:07

1 Answers1

0

A form only submits the values of its successful controls (as name/value pairs based on the controls name and value attributes) so if you do not generate a control for the ObjectId properties, they will not be submitted.

However, you current use of foreach loop will not allow you to obtain any meaning information from the data which is posted because all your names are identical and there is no way to reliable match up which value belongs to which item in the collection. Instead use a for loop or EditorTemplate in the view and bind to your model, rather than using FormCollection.

The view should be

@for (int i = 0; i < Model.AllDataObjects.Count; i++)
{
    <tr>
        <td>@Html.DisplayFor(m => m.AllDataObjects[i].Name)</td>
        <td>@Html.DisplayFor(m => m.AllDataObjects[i].Data)</td>
        <td>
            @Html.HiddenFor(m => m.AllDataObjects[i].ObjectId)
            @if (Model.AllDataObjects[i].Rule.Contains("Yes;No"))
            {
                @Html.DropDownListFor(m => m.AllDataObjects[i].Value, new SelectList(new string[]{ "Yes", "No" }));
            }
            else
            {
                @Html.TextAreaFor(m => m.AllDataObjects[i].Value, new { style = "width: 400px;", @rows = 5 })
            }
        </td>
    </tr>
}

And assuming the model in the view is @model MyModel, change the POST method to

[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(MyModel model) 

and the value of model.AllDataObjects will contain a collection with its ObjectId and Value properties correctly bound.

For more information on why using a foreach loop will not work, refer to this answer.

Community
  • 1
  • 1