我怎样才能拥有多个表单,每个表单都是从项目列表中生成的?

how can I have multiple forms each generated from a list of items?

在 MVC 应用程序中,我有一个试题列表,我想在同一页面上向用户展示其中的一小部分,但每个答案都可以单独提交。 所以我的页面看起来像这样....

查看代码为....

    @model List<QuestionResponseVM>
    @for (int i = 0; i < Model.Count(); i++)
    {
      using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
      {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(model => model[i].QuestionID)
        <tr>
          <td width="35%">
            @Html.Raw(Model[i].QuestionText)
            @Html.HiddenFor(model => model[i].QuestionText)
          </td>
          <td>
            @Html.TextAreaFor(model => model[i].Response, new { @name = "DisplayTextEdit", @id = "DisplayTextEdit", @rows = 1, @cols = 80 })
          </td>
          <td width="30%">
            <div class="form-group">
              <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-primary" />
              </div>
            </div>

          </td>
        </tr>
      }
    }

我的问题是我只能获取数据 returned 到问题 1 的 POST 方法。
这是控制器代码....

  public class ChecksController : Controller  
  {  
  
    public ActionResult CheckQuestions()
    {
      return View(LoadQuestions());
    }
  
    // POST: Checks
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CheckQuestions(List<QuestionResponseVM> questResponses)
    {
      List<QuestionResponseVM> testList = new List<QuestionResponseVM>();
  
      if (ModelState.IsValid)
      {
        testList = LoadQuestions(questResponses[0].QuestionID, questResponses[0].Response);
      }
  
      return View(testList);
  
    }
  
    private List<QuestionResponseVM> LoadQuestions (int _QuestionID = -1, string _Response = "")
    {
      List<QuestionResponseVM> thisList = new List<QuestionResponseVM>();
  
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 1,
        QuestionText = "Question 1",
        Response = (_QuestionID == 1 ? _Response : "")
      });
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 2,
        QuestionText = "Question 2",
        Response = (_QuestionID == 2 ? _Response : "")
      });
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 3,
        QuestionText = "Question 3",
        Response = (_QuestionID == 3 ? _Response : "")
      });
  
      return thisList;
    }
  }
  1. 如果 Controller POST 方法的参数为 QuestionResponseVM questResponses,这是我所期待(希望)的,那么 null 是 return无论单击哪个“保存”按钮,都会从视图中编辑。

  2. 但是,如果我将参数更改为列表(即 List questResponses),那么问题 1 的“保存”按钮 returns 包含单个项目和正确数据的列表。但是,任何其他“保存”按钮(例如问题 2 或问题 3)return 都是空列表。

场景 1 的行为对我来说似乎违反直觉,因为“开始表单”设置为 return 单个模型项(模型实例),即“模型 [i]”。
在场景 2 中,我只是不明白为什么它适用于第一种形式(“保存”按钮)而不适用于其他形式。

我认为我不需要使用 JScript 或 AJAX 来执行此操作。

但很明显,我不是在这里“连接一些点”。
有人可以解释我观察到的行为,并可能会在正确的方向上推动我满足这个要求吗?

如有任何帮助,我将不胜感激。

在回答你的问题之前,我不明白 new {questResponses = Model[i] })) 在你的表单中做了什么:

using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
{
    ...
}

Model[i] 是一个复杂的对象。你得到的只是对象的名称:


问题 1:如果 Controller POST 方法只有一个参数

由于您使用 for 循环生成每个表单和表单中的输入,因此这些输入的名称将采用 [INDEX].NAME:

的形式

默认情况下,模型绑定会将这些输入(QuestionId、QuestionText 和 Response)绑定到匹配的对象。 QuestionResponseViewModel 确实匹配。问题是 [INDEX]. 前缀。

为了使默认模型绑定起作用,您在 POST 方法中声明的参数名称必须调用 [INDEX],即第一种形式为 [0][1] 第二种形式等等:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM [0])
{
    ...
}

但是你知道我们不能在 C# 中声明类似的东西。


Q1 的修复

您可以使用 foreach 来生成每个表单,而不是使用常规的 for 循环。这样一来,您就无需为每个表单命名一个不断变化的参数。

这里的另一个“GOTYOU”是控制器中的参数必须匹配你在for循环中为每个QuestionResponseViewModel:[=34=声明的变量]

@foreach (var qrVM in Model)
{
    using(Html.BeginForm("..."))
    {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(x => qrVM.QuestionId)

        <tr>
            <td>
                @Html.DisplayFor(x => qrVM.QuestionId)
                @Html.HiddenFor(x => qrVM.QuestionId)
            </td>
            ...
        </tr>
    }
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM qrVM)
{
    // If you name the parameter something else, it won't bind!
    ...
}

如果您考虑一下,这是有道理的,因为您知道表单会将 post 带有 qrVM.QuestionIdqrVM.QuestionText 等键的数据返回到服务器。默认模型绑定将搜索具有这些属性并命名为 qrVM.

的模型


Q2:将参数更改为列表

当第一个表单 post 返回到服务器时,请求正文中的表单数据将如下所示:

[0].RequestionId: 1
[0].RequestionText: Question 1
[0].Response: xxx

MVC 模型绑定仍然足够智能,并且认为您post正在处理您声明的列表的第一项。因此,您将看到 List<QuestionResponseVM> questResponses 捕获第一个表单的正确数据。

那么第二种和第三种形式呢?例如,如果您在第二个表单上提交数据,则请求正文中的表单数据将如下所示:

[1].RequestionId: 2
[1].RequestionText: Question 2
[1].Response: xxx

MVC 模型绑定将其视为列表的第 2 项,但第 1 项在哪里?它变得混乱,因此无法将数据绑定到参数。因此,您将从参数 List<QuestionResponseVM> questResponses.

中看到 NULL

我的 2 美分

  1. 您实际上不能像这样将表单放在 table 行内或 table 行之间。它被认为是无效的 HTML 结构。无论如何,使用 table 作为在页面上显示数据的结构从来都不是一个好主意。相反,您可以使用 Bootstrap 的行和列。

  2. 我不知道为什么或是什么让你认为你不应该需要 AJAX。您的情况就像采用 AJAX 方法的最佳方案!例如,使用 AJAX,用户可以单独保存每个问题的回答。页面不必刷新。

您在问题 1 上的保存按钮正在将表单提交给控制器。您需要在一组问题的末尾有一个 Save/Submit 按钮并使用 FormCollection 对象,或者花时间为每个按钮上的单击事件设置 JQuery/Ajax 并删除表单元素。如果底部的按钮变成 'Next' 然后提交给控制器以获取下一组相关问题,您可能会同时拥有两者。