How to validate and post data from dynamic form in ASP.NET MVC 2
I have an existing ASP.NET MVC 2 application that I've been asked to extend. I am adding a new feature to the site where I generate an employee assessment form based on a dynamic list of questions retrieved from our HR system. I have everything working with the exception of validation and posting the responses back to the site. Here's some details:
- I retrieve a list of "Questions" from our back-end system via a web service call.
- Each "Question" contains the text to display as well as the following settings:
- The question Type (corresponds to textbox, textarea, radio button list or checkbox list)
- If comments are allowed
- If an answer is required
- When applicable, the list of possible responses
To generate the form, I use a for-each loop over the list of Questions. I use the value of the QuestionType property to determine which partial view to render (one for each of the types). For example, if QuestionType == SingleChoice, that partial renders the choices as a radio button list. If comments are allowed for the question, I also render an additional textarea field to hold the user's comments.
As I said, rendering the form is working fine but now I need to:
A. Enforce when an answer is required. I'm using DataAnnotations for validation everywhere else in the solution but since I'm not working against a static model, I don't see how I can do开发者_JAVA百科 that.
B. Post the results back to the site. For each question, there can be text entered into a textbox or textarea, a selected value for a radio button list or multiple selected values for a checkbox list. Plus, each question could also have additional text sent back in the form of a comment.
All of the examples that I've seen working with dynamic "lists" are only concerned with posting a single value for each field and it is always the same type (e.g. a list of textboxes). With the variations I have to support, plus the need to send back the entered/selected value(s) and a comment for each question, I'm stumped.
Any guidance is appreciated.
I've just finished completing exactly the same task.
I chose to write a custom model binder for my dynamic form object. The model binder pulled out a bunch of prefixed form keys for hidden fields which contained some delimited meta data about the question (i.e IsRequired, QuestionType, QuestionId etc etc)
I'm using MVC3 but I think this should all work in MVC2.
I created a ModelBinder like:
public class DynamicFormModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Create the object to be bound to (I had a kind of form object
// with a simple list of answer objects
DynamicForm form = new DynamicForm(new List<Answer>());
HttpRequestBase request = controllerContext.HttpContext.Request;
var keys = request.Form.AllKeys.Where(k => k.StartsWith("MyFormsKeyPrefix_Meta_"));
foreach (var key in keys)
{
// Loop over each question's meta data. Metadata will always be present
// even if the user hasn't selected an answer as it's a hidden field
// TODO: Split the meta data and pull out IsRequired, QuestionType etc
// TODO: Get all the posted form values for the question (these values
// will come from textboxes, dropdowns, checkboxes etc)
// Use a prefix like: MyFormsKeyPrefix_Answer_{QuestionId}
// textboxes & dropdowns will only ever have one value
// but checkboxes could have multiple
// TODO: If it's a mandatory question then ensure there is at least
// one posted value that is not an empty string
// If there is a validation error then add it to the model state
bindingContext.ModelState.AddModelError(key, "Field is required");
foreach(var answerHtmlName in answerHtmlNames)
{
// TODO: Loop over each posted answer and create some kind of nice
// Answer object which holds the QuestionId, AnswerId, AnswerOptionId
// and Value etc.
// Add the answer to the forms answers list
form.Answers.Add(answer);
}
}
return form;
}
}
I register the ModelBinder in Global.asax using the following:
ModelBinders.Binders.Add(typeof(DynamicForm), new DynamicFormModelBinder());
So, the action method that recieves the form post looks something like:
public ActionResult ProcessForm(DynamicForm form) {
if(ModelState.IsValid)
{
DynamicFormService.Process(form);
return RedirectToAction("TheHttpGetAction");
}
return TheHttpGetAction();
}
精彩评论