我怎样才能拥有多个表单,每个表单都是从项目列表中生成的?
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;
}
}
如果 Controller POST 方法的参数为 QuestionResponseVM questResponses,这是我所期待(希望)的,那么 null 是 return无论单击哪个“保存”按钮,都会从视图中编辑。
但是,如果我将参数更改为列表(即 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.QuestionId
、qrVM.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 美分
您实际上不能像这样将表单放在 table 行内或 table 行之间。它被认为是无效的 HTML 结构。无论如何,使用 table 作为在页面上显示数据的结构从来都不是一个好主意。相反,您可以使用 Bootstrap 的行和列。
我不知道为什么或是什么让你认为你不应该需要 AJAX。您的情况就像采用 AJAX 方法的最佳方案!例如,使用 AJAX,用户可以单独保存每个问题的回答。页面不必刷新。
您在问题 1 上的保存按钮正在将表单提交给控制器。您需要在一组问题的末尾有一个 Save/Submit 按钮并使用 FormCollection 对象,或者花时间为每个按钮上的单击事件设置 JQuery/Ajax 并删除表单元素。如果底部的按钮变成 'Next' 然后提交给控制器以获取下一组相关问题,您可能会同时拥有两者。
在 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;
}
}
如果 Controller POST 方法的参数为 QuestionResponseVM questResponses,这是我所期待(希望)的,那么 null 是 return无论单击哪个“保存”按钮,都会从视图中编辑。
但是,如果我将参数更改为列表(即 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.QuestionId
、qrVM.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
.
我的 2 美分
您实际上不能像这样将表单放在 table 行内或 table 行之间。它被认为是无效的 HTML 结构。无论如何,使用 table 作为在页面上显示数据的结构从来都不是一个好主意。相反,您可以使用 Bootstrap 的行和列。
我不知道为什么或是什么让你认为你不应该需要 AJAX。您的情况就像采用 AJAX 方法的最佳方案!例如,使用 AJAX,用户可以单独保存每个问题的回答。页面不必刷新。
您在问题 1 上的保存按钮正在将表单提交给控制器。您需要在一组问题的末尾有一个 Save/Submit 按钮并使用 FormCollection 对象,或者花时间为每个按钮上的单击事件设置 JQuery/Ajax 并删除表单元素。如果底部的按钮变成 'Next' 然后提交给控制器以获取下一组相关问题,您可能会同时拥有两者。