How to pass strongly typed results back to ActionResult?
I have a view that is strongly typed:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
Works great to generate the view, but when I post, I have an ActionResult defined:
[AcceptVerbs(HttpVerbs.Post)]
public Ac开发者_开发问答tionResult Next(MPKwithMVC.Models.SmartFormViewModel model)
{ .. }
Which I would imagine get hit when my next button is clicked (it works if I change the argument to a FormsCollection). I instead get a message saying "No parameterless constructor defined for this object".
What am I doing wrong?
My SmartFormsViewModel is:
[Serializable]
public class SmartFormViewModel
{
public List<Question> Questions { get; set; }
public List<Answer> Answers { get; set; }
public SmartFormViewModel(List<Question> questions, List<Answer> answers)
{
this.Questions = questions;
this.Answers = answers;
}
public SmartFormViewModel()
{
}
}
And here is the View:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
<%@ Import Namespace="MPKwithMVC.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
SmartForms
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Questionaire</h2>
<% using (Html.BeginForm("Next", "SmartForms"))
{ %>
<div style="float: left; margin-right: 2em;">
<% Html.RenderPartial("NavigationPanel", Model); %>
</div>
<div>
<table>
<%
foreach (Question question in (Model.Questions))
{ %>
<tr>
<td>
<div style="text-align: right; width: 20em;">
<%= Html.Encode(question.QuestionText)%>
</div>
</td>
<td>
<div style="float: left;">
<% if (question.QuestionType == 1)
{ %>
<%= Html.TextBoxFor(model => model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
<% } %>
<% if (question.QuestionType == 2)
{ %>
<%= Html.RadioButton("yn" + question.QuestionID, "Yes", false)%>Yes
<%= Html.RadioButton("yn" + question.QuestionID, "No", true)%>No
<% } %>
</div>
<% if (question.Required == true)
{ %>
<div style="color: Red; float: right; margin-left: 3px;">
*</div>
<% } %>
</td>
</tr>
<%
} %>
<tr>
<td>
<%
if (ViewData["errorMsg"] != null)
{%>
<div style="color:Red;">
<%= Html.Encode(ViewData["errorMsg"].ToString()) %>
</div>
<% } %>
</td>
<td>
<div style="margin-top: 1em;">
<button name="button" value="next">Next</button>
</div>
</td>
</tr>
</table>
</div>
<% } %>
</asp:Content>
Have a look at the input names that are being generated within your HTML. I think you have an issue with the naming of your controls thus the default model binding is failing since you mentioned that the using FormCollection
works correctly. I am making this assumption since I don't know what your Questions and Answers classes look like
<%= Html.TextBoxFor(model =>
model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
Won't this render something similar to the following; which if I'm not mistaken will not bind to your model. The same applies for the RadioButtons
.
<input type="text" name="Answers[0].AnswerValue" id="Answers_0__AnswerValue" value="somevalues"/>
The RadioButton helper should be
<%= Html.RadioButton("Questions[" + question.QuestionID + "].ID", "Yes", false)%> // you now get a list of questions
<input type="radio" name="Questions[1].ID" id="Questions_1__ID" value="No" checked="checked"/>
There are some ways you can try to resolve this:
- Correctly name your input controls so that they match you ViewModel. (This includes posting back all the required fields for you model - I think that that default values are used when the model binding occurs if its not posted)
- Create a custom model binder
- You may need to tell the Binder what the Prefix of the input fields are. (
[Bind]
attribute) to specifically include or exclude form fields. - Create a new Model containing the values you expect to post back
I think that overall that your approach needs to slightly change. From the info provided, your Q&As are closely related. Depending on the question type your 'answer' is either boolean or freetext. At the moment your are not posting a List<Questions>
back to the server. Answers, yes, but they are not I don't think that it is recognised as List<Answers>
.
Haacked has a post which I think is related to your issue and this SO question further indicates that it may still applly to ASP-MVC-2.
As the error message suggest, your SmartFormViewModel class needs to contain a parameterless constructor.
This is a great example of what you would like to do
I would also suggest you use the strongly typed helpers from Html extensions to generate your form fields.
e.g.
Html.HiddenFor(x => x.SomeField)
Html.TextBoxFor(x => x.SomeEditableField)
Make sure that your <form>
's action
points to your Next
method.
精彩评论