Having some issues trying to model bind to my view model.
The basic viewmodel is thus:
public class RegistrationVM
{
public TravelHistoryVM TravelHistory {get;set;}
public UserDetailVM UserDetails {get;set;}
public ICollection<HandsetDevicesVM> ExistingDevices {get;set;}
}
A wrapper for 3 further viewmodels, each containing the usual band of strings, ints and bools. Ignore the collection of HandsetDevicesVM for now.
I have the following view (sample for clarity)
@using (Html.BeginForm())
{
@Html.ValidationSummary(false)
@Html.LabelFor(model => model.TravelHistory.DoNotTravel)
@Html.CheckBoxFor(model => model.TravelHistory.DoNotTravel)
@Html.EditorFor(model => model.UserDetails, "UserDetailsHidden")
}
The ‘TravelHistory’ object is being bound fine, no issues.
The ‘UserDetails’ object is always just null when data is posted back.
The template is definitely rendering, I can tell from the HTML. The template itself is given below:
@model Foo.Bar.UserDetailVM
@Html.HiddenFor(model => model.EmailAddress)
@Html.HiddenFor(model => model.FirstName)
@Html.HiddenFor(model => model.Surname)
@* etc etc *@
The HTML generated is
<input data-val="true" data-val-required="The Email Address field is required." id="UserDetails_EmailAddress" name="UserDetails.EmailAddress" type="hidden" value="stack@overflow.com" />
<input data-val="true" data-val-required="The First Name field is required." id="UserDetails_FirstName" name="UserDetails.FirstName" type="hidden" value="Stack" />
<input data-val="true" data-val-required="The Surname field is required." id="UserDetails_Surname" name="UserDetails.Surname" type="hidden" value="Overflow" />
I am using this EditorTemplate on another view with no issues (it binds to that viewmodel no problem).
Finally, using Fiddler I can ascertain the information is being posted.. I think
UserDetails.EmailAddress=stack%40overflow.com.test&UserDetails.FirstName=stack&UserDetails.Surname=overflow
Any ideas? Where should I be concentrating my debugging efforts?
Edit1: just noticed this from the ModelState object
AttemptedValue: 'Foo.Bar.UserDetailVM'
So it seems it is trying to bind the string value ‘Foo.Bar.UserDetailVM’ to the object and (obviously) failing.
Edit2:
[HttpPost]
public ActionResult Travel(RegistrationVM model)
As requested, the action I am POSTing to
Edit3:
Ahhh.. OK going back a stage, this is the action method that serves up the View
[HttpGet]
public ActionResult Travel(PreRegistrationVM model)
{
if (TempData["PreRegistrationVM"] != null)
{
model = (PreRegistrationVM)TempData["PreRegistrationVM"];
}
var newModel = new RegistrationVM(model);
return View(newModel);
}
This is what I have been using (and thus has been part of the process that does not work). Essentially the information from a prior step is saved in the TempData and picked up in this method. It is then used to instantiate the VM as above. At this point (if I insert a breakpoint here), the model is populated with data as I expect.
If I amend that to the following:
[HttpGet]
public ActionResult Travel(PreRegistrationVM model)
{
var newModel = new RegistrationVM
{
FirstName = "Stack",
Surname = "Overflow"
};
return View(newModel);
}
It binds as expected. Is TempData the culprit here?
Quick background check: this was a problem encountered whilst creating a ‘wizard’ step by step registration process. I wanted to avoid saving information to the database until the registration was completed. Hence, the idea was to use the TempData collection to store information between Actions, and to use RedirectToAction to move from step to step.
For what it is worth, I amended the method from using TempData like so
To retrieve the information from the database once per step in the registration process.
I’m not entirely sure why the TempData approach resulted in this, would be interested in people more learned than I offering reasons.
Thanks for all who answered, helped me track it down.