I ran across a problem today where the Code value of a nested object in one of our forms was being changed to an incorrect value. After some digging, I discovered that it is being assigned the value of the Parent object Code, only after POSTing, and only when I try to set the Name attribute explicitly with Html.TextBoxFor's second object parameter.
I setup a simple MVC(Version 5.2.2.0) project to isolate the issue. Here is the code for that.
Models
public class Parent
{
public string Code { get; set; }
public Child Child { get; set; }
}
public class Child
{
public string Code { get; set; }
}
Controllers
public class ParentController : Controller
{
public ActionResult Show()
{
var child = new Child() { Code = "999"};
var parent = new Parent() { Code = "1", Child = child };
return View("Show", parent);
}
public ActionResult Update(Parent parent)
{
return View("Show", parent);
}
}
Views/Parent/Show
@model TextBoxForBugTest.Models.Parent
@using (Html.BeginForm("Update", "Parent"))
{
@Html.TextBoxFor(o => o.Code)
@Html.Partial("~/Views/Child/Show.cshtml", Model.Child)
<button type="submit">Submit</button>
}
Views/Child/Show
@model TextBoxForBugTest.Models.Child
@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })
When I first load /Parent/Show, I see the correct values in the inputs: 1(Code), and 999(Child.Code).
However, after returning from the Update Action Method after submitting the form, Child.Code has been assigned the Value "1" - the Parent Code.
I've found that I can fix the issue by setting the HtmlFieldPrefix.
@model TextBoxForBugTest.Models.Child
@{ Html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "Child"; }
@Html.TextBoxFor(o => o.Code)
or by using a local variable
@model TextBoxForBugTest.Models.Child
@{ var theCode = Model.Code; }
@Html.TextBoxFor(o => theCode, new { Name = "Child.Code" })
but I'd like to understand why. What is going on here? Why is Child.Code being assigned the value of Parent.Code after POSTing?
I also found some related questions that get into using extensions, but they seem to be answering different questions
ASP.NET MVC partial views: input name prefixes
ASP.MVC 3 Razor Add Model Prefix in the Html.PartialView extension
***Edit - It's clear from the answers that I did a poor job of stating my actual question, so I'll attempt to clarify a bit more here.
The problem I was seeing that was leading to an end user identified bug was that
@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })
was generating html with a different "value" the second time it was called(after POSTing).
I was able to solve that problem by setting the Html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix. Stephen Muecke also pointed out another - probably better - solution to that problem in Editor Templates.
What I was trying to ask though was this:
Why does
@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })
generate
<input name="Child.Code" id="Code" type="text" value="999">
the first time (/Parent/Show), but then generate
<input name="Child.Code" id="Code" type="text" value="1">
the second time (after POSTing to /Parent/Update)?
The Form Data that gets POSTed is
and the binded Model in
public ActionResult Update(Parent parent)
{
return View("Show", parent);
}
has the expected values of Parent.Code == 1 and Child.Code == 999.
I think Stephen Muecke is probably close to the answer I'm looking for in his comment
Note also that the new { Name = "Child.Code" } hack does not change the id attribute and you have invalid html. – Stephen Muecke
Indeed, using
@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })
, I end up with 2 inputs with id="Code", which is invalid according to the spec.
Even knowing that though, I still don't understand why the value attribute generated by TextBoxFor is different based on whether I'm GETing /Parent/Show or POSTing to /Parent/Update.