Reputation: 932
I am building a wizard - a series of form steps.
Each step of the wizard are submitted to the same action method (it needs to be highly extendable), and all step viewmodels inherit from the same base model. They are bound to their concrete type by a custom model binder. The submit action saves the form fields and returns the next step (or the same in case of errors).
It works well. However, at one point the user needs to supply work information. The step immediately following primary work is secondary work information - same form, same properties. The viewmodel for the second part inherits from the primary viewmodel without implementing any individual properties (the class definition is empty).
After the primary form is submitted, the secondary viewmodel is returned with values reset. However, for some reason, the form is shown with the first viewmodels values.
And the really strange thing is, the model values are overridden by Request.Form values for all EditorFor calls, but not for specific calls:
// This displays correctly, showing the current model values
<div>Model says: @Model.AuthorizationNumber</div>
<div><input type="text" value="@Model.AuthorizationNumber"/></div>
// This displays the Request.Form value from the previous step.
@Html.EditorFor(x => x.AuthorizationNumber)
In the above, the authorization number is shown correctly for the first two lines (constructor values), but the third is incorrect (showing the Request.Form value).
What could be the problem?
EDIT 1
On request, the controller method handling the submit.
public ActionResult Index(StepPathState model = null)
{
var isPreviousSubmit = Request.Form.AllKeys.Contains("submit-previous");
if (model == null || model.Step == null) return View(Steps.GetFirstPathResult());
// submit the step data
var result = Steps.SubmitStepData(model, isPreviousSubmit);
if (result.Status is StepSuccessAndFinish)
{
return Finish(result);
}
var failureStatus = result.Status as StepActionStatusFailure;
if (failureStatus != null)
{
foreach (var error in failureStatus.Errors)
{
ModelState.AddModelError(error.Property, error.ErrorMessage);
}
}
return View(result);
}
I may have found the answer though: http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx
Upvotes: 1
Views: 249
Reputation: 932
Solution: The good and the ugly
As the article linked above describes, it seems that MVC favors the modelstate values when returning from a POST. This is because re-displaying the form in a POST should always indicate a validation failure. So the correct solution is to save, redirect to a GET request and display the new form. This would fix the problem, and is what I am going to do when I have time to refactor.
A temporary solution is to clear the modelstate. This will cause it to fallback back to the model. This is my temporary fix:
if (failureStatus == null && ModelState.IsValid)
{
ModelState.Clear();
}
return View(result);
The danger here is that I have cleared the modelstate. And what might that entail in the long run? New bug adventures to come. But I am on the clock.
Upvotes: 2