I have a model object structure with a Foo
class that contains a Bar
with a string value.
public class Foo
{
public Bar Bar;
}
public class Bar
{
public string Value { get; set; }
}
And a view model that uses that structure like this
public class HomeModel
{
public Foo Foo;
}
I then have a form in view that in Razor looks something like this.
<body>
<div>
@using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
<fieldset>
@Html.TextBoxFor(m => m.Foo.Bar.Value)
<input type="submit" value="Send"/>
</fieldset>
}
</div>
</body>
In html that becomes.
<form action="/Home/Save" method="post">
<fieldset>
<input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
<input type="submit" value="Send">
</fieldset>
</form>
Finally the controller to handle the post loos like this
[HttpPost]
public ActionResult Save(Foo foo)
{
// Magic happends here
return RedirectToAction("Index");
}
My problem is that Bar
in Foo
is null
once it hits the Save
controller action (Foo
is created but with an null
Bar
field).
I thought the model binder in MVC would be able to create the Foo
and the Bar
object and set the Value
property as long as it looks like the above. What am I missing?
I also know my view model is a bit over complicated and could be simpler but I for what I'm trying to do I'd really help me if I could use the deeper object structure. The examples above uses ASP.NET 5.
Firstly, the DefaultModelBinder
will not bind to fields so you need to use properties
public class HomeModel
{
public Foo Foo { get; set; }
}
Secondly, the helpers are generating controls based on HomeModel
but you posting back to Foo
. Either change the POST method to
[HttpPost]
public ActionResult Save(HomeModel model)
or use the BindAttribute
to specify the Prefix
(which essentially strips the value of prefix from the posted values - so Foo.Bar.Value
becomes Bar.Value
for the purposes of binding)
[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)
Note also that you should not name the method parameter with the same name as one of your properties otherwise binding will fail and your model will be null.
I just discovered another reason this can happen, which is if your property is named Settings
! Consider the following View model:
public class SomeVM
{
public SomeSettings DSettings { get; set; } // named this way it will work
public SomeSettings Settings { get; set; } // property named 'Settings' won't bind!
public bool ResetToDefault { get; set; }
}
In code, if you bind to the Settings
property, it fails to bind (not just on post but even on generating the form). If you rename Settings
to DSettings
(etc) it suddenly works again.
I had the same problem and after I followed @Stephen Muecke steps I realized that the problem was caused because my inputs were disabled (I was disabling them with JQuery on document ready) as you can see it here: How do I submit disabled input in ASP.NET MVC?. At the end I used read-only instead of disabled attribute and all the values were sent successfully to the controller.
I had the same problem, but once I created a HIDDEN FIELD for the foreign-key...it all worked just fine...
FORM EXAMPLE:
@using (Html.BeginForm("save", "meter", FormMethod.Post))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
@Html.HiddenFor(model => Model.Entity.Id)
@Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
@Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
@Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)
... all your awesome controls go here ...
}
ACTION EXAMPLE:
// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
{
... world-saving & amazing logic goes here ...
}
PRETTY PICTURES: