Incorrect model property value rendered in partial view
Asked Answered
L

4

6

I have a strongly-typed partial view whose model contains a property with the same name as the parent page's view model. For some reason the rendering engine is rendering the parent view model value, not the expected value (well, the value I expect at least!)

Parent page view model extract:

public class ParentPageViewModel
{
    public int Id { get; set; } // problem property
    ...
    public IEnumerable<ChildViewModel> Children { get; set; }
}

Child page view model extract:

public class ChildViewModel
{
    public int Id { get; set; } // problem property
    ...
}

Parent page extract (Razor):

@model ParentPageViewModel
...
@foreach (var item in Model.Children)
{
    @Html.Partial("MyPartialView", item)
}
...

Partial view extract:

@model ChildViewModel
...
<form ...>
    @Html.HiddenFor(m => m.Id) // problem here - get ParentPageViewModel.ID not ChildViewModel.Id
</form>
...

So basically in my rendered output, my hidden field has the value of the parent view model element, NOT the value passed to the partial view. It's definitely being caused by the name, as changing the name of @ChildViewModel.Id@ to something like @ChildViewModel.ChildId@ makes it work as expected. Interestingly, when inspecting the view model values in the debugger I do see the correct values; it's only the rendered output that's wrong.

Is there a way round this or 'correct' way of doing what I'm trying to do (I'm rendering mini forms in a table for ajax validation/posting of updates to table rows)

Thanks,

Tim

Lautrec answered 7/8, 2012 at 10:16 Comment(1)
The HtmlHelpers will take the values from the ModelState dictionary first (even over the value in the Model) - looks like that is the reason for this behaviour.Handling
H
14

I think changing your call to this will solve the problem:

@Html.Partial("MyPartialView", item, new ViewDataDictionary())

The child view is picking up the value from the ViewData dictionary - so this passes in a new dictionary to the child view (hattip danludwig).

Handling answered 7/8, 2012 at 11:23 Comment(3)
+1 I was going to add this to my answer too, but had to leave before I could finish. You are right, the child model is getting the ID from the parent's viewdata dictionary.Clachan
Yes this works, although I actually did use my own answer (hand-crafted <input> tag) as my partial view is used all over the place so wwanted to avoid the risk of forgetting the 'new ViewDataDictionary()' part. Thanks.Lautrec
@TimCroydon to prevent that risk you could create a HtmlHelper extension method that does it - also means you can hard-code the 'MyPartialView' name in one place too.Handling
F
3

I know this an old post. But I figured since I landed here when I was facing the same problem, I might as well contribute. My issue was a little different. In my case, the main view's Id was incorrect after a partial view action that required the whole page/view to be refreshed was triggered. I solved the problem with ModelState.Clear

ModelState.Clear();
return View("MyPartialView", model);  //call main view from partial view action
Ferromanganese answered 26/6, 2016 at 8:8 Comment(0)
L
2

Found a solution, just manually creating the hidden field, e.g.:

<input type="hidden" name="Id" value="@Model.Id" />

instead of using Html.HiddenFor.

(I won't mark this as answered for a while in case there are any other solutions or anyone can explain the problem)

Lautrec answered 7/8, 2012 at 11:18 Comment(0)
C
0

Create a file named ChildViewModel.cshtml in Views/Shared/EditorTemplates. Put your partial view into that file:

in ~/Views/Shared/EditorTemplates/ChildViewModel.cshtml

@model ChildViewModel
...
<form ...>
    @Html.HiddenFor(m => m.Id)
</form>
...

Then, render it like this:

@model ParentPageViewModel
...
@foreach (var item in Model.Children)
{
    @Html.EditorFor(m => item)
}
...

Or, if you'd rather keep the view as a partial and not as an editor template, use Simon's answer.

Clachan answered 7/8, 2012 at 11:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.