Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Html.HiddenFor binding to wrong element

I have an ASP.NET MVC application that displays a list of items. In my view page I loop over the items and render each item with partial view, like so:

@foreach(var item in Model.items)
{
   <li>
       @Html.Partial("ItemView", item)
   </li>
}

In the item view, I wrap each item with a form that has a 'Delete' button, like this:

@using(Html.BeginForm(...))
{
    @Html.HiddenFor(m=>m.Id)
    <label>@Model.Name (@Model.Id)</label>
    <input type="submit" value="Delete"/>
}

The items are rendered properly, the resulting page has a nice list of all the items with their proper names and IDs displayed.

EDIT: The same happens with @Hidden, apparently, contrary to what I wrote before.

In addition, this only happens the second time the form is rendered (that is, after one of the Delete buttons is clicked), the first time everything is working properly. My action methods looks like this:

public ActionResult AllItems()
{
    var model = new AllItemsModel();
    return PartialView(model);
}

public ActionResult Delete(DeleteModel model)
{
    .... Perform the delete ...
    return PartialView("AllItems", new AllItemsModel());
}

Why is this happening?

like image 884
zmbq Avatar asked Oct 19 '12 08:10

zmbq


1 Answers

I suspect that this happens because you already have an Id parameter in your RouteData:

public ActionResult SomeAction(int id)
{
    var model = ...
    return View(model);
}

and you have requested the page with /somecontroller/someaction/123. The HiddenFor helper now uses the Id from the route values and not the id of the item. Try renaming the property on your item view model to something different than id. For example ItemId.

Another possibility is that the problem occurs only after the postback and not when the page is initially rendered. Showing your POST action might help in exploring this possibility further.


UPDATE:

Alright, now that you have shown your POST action things are much more clear:

public ActionResult Delete(DeleteModel model)
{
    .... Perform the delete ...
    return PartialView("AllItems", new AllItemsModel());
}

you are basically creating a new view model here and passing it to the partial view. But HTML helpers always use the value from the ModelState when binding. And only after that the value from your view model. So if you intend to modify properties on your model inside your POST action make sure that you have removed this value from the ModelState first. In your example since you have completely scratched the entire view model (by creating a new AllItemsModel()) you could clear the entire ModelState:

public ActionResult Delete(DeleteModel model)
{
    .... Perform the delete ...

    // Clear the modelstate otherwise the view will use the values that were initially posted
    // and not the values from your view model
    ModelState.Clear();
    return PartialView("AllItems", new AllItemsModel());
}

This behavior is by design and applies to all HTML helpers, not only the HiddenFor helper.

like image 186
Darin Dimitrov Avatar answered Sep 28 '22 07:09

Darin Dimitrov