2

我有一个已被要求扩展的现有 ASP.NET MVC 2 应用程序。我正在向站点添加一项新功能,在该站点中,我根据从我们的人力资源系统检索到的问题的动态列表生成员工评估表。除了验证和将回复发布回网站之外,我的一切都在工作。这里有一些细节:

  1. 我通过 Web 服务调用从我们的后端系统检索“问题”列表。
  2. 每个“问题”都包含要显示的文本以及以下设置:
    • 问题类型(对应于文本框、文本区域、单选按钮列表或复选框列表)
    • 如果允许评论
    • 如果需要回答
    • 适用时,可能的响应列表

为了生成表单,我在问题列表上使用了一个 for-each 循环。我使用 QuestionType 属性的值来确定要呈现的局部视图(每种类型一个)。例如,如果 QuestionType == SingleChoice,则部分将选项呈现为单选按钮列表。如果问题允许评论,我还会渲染一个额外的 textarea 字段来保存用户的评论。

正如我所说,渲染表单工作正常,但现在我需要:

A. 在需要回答时执行。我在解决方案中的其他任何地方都使用 DataAnnotations 进行验证,但是由于我没有针对静态模型工作,所以我不知道该怎么做。

B. 将结果发布回网站。对于每个问题,可以在文本框或文本区域中输入文本、单选按钮列表的选定值或复选框列表的多个选定值。此外,每个问题还可以以评论的形式发回额外的文本。

我见过的使用动态“列表”的所有示例都只涉及为每个字段发布单个值,并且它始终是相同的类型(例如文本框列表)。有了我必须支持的变化,加上需要发回输入/选择的值和每个问题的评论,我很难过。

任何指导表示赞赏。

4

1 回答 1

1

我刚刚完成了完全相同的任务。

我选择为我的动态表单对象编写一个自定义模型绑定器。模型绑定器为隐藏字段提取了一堆前缀表单键,其中包含一些关于问题的分隔元数据(即 IsRequired、QuestionType、QuestionId 等)

我正在使用 MVC3,但我认为这应该在 MVC2 中都可以工作。

我创建了一个 ModelBinder,如:

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;
    }

}

我使用以下方法在 Global.asax 中注册了 ModelBinder:

ModelBinders.Binders.Add(typeof(DynamicForm), new DynamicFormModelBinder());

因此,接收表单 post 的操作方法如下所示:

public ActionResult ProcessForm(DynamicForm form) {
    if(ModelState.IsValid) 
    {
        DynamicFormService.Process(form);

        return RedirectToAction("TheHttpGetAction");
    }
    return TheHttpGetAction();
}
于 2011-05-09T16:30:19.877 回答