如何把通过foreach生成的输入传递给控制器?
我的测试包含一个存放在数据库中的题目列表。事先并不知道有多少道题,但提交测试时只有当用户为每道题都给出答案时才会被接受(就像Google表单一样)。
根据技术规范,我需要使用单选按钮来选择答案。遗憾的是,我在ASP.NET Core MVC方面经验并不丰富,难以设想模型应该如何组织,才能在点击“Submit”按钮时,在控制器的 Vote 动作中成功检索到所有RadioButton组。
我尝试用这段视图生成表单(单选按钮组):
@{
string title = "Тест";
ViewBag.Title = title;
IEnumerable<Question>? questions = ViewBag.Questions;
IEnumerable<Answer>? answers = ViewBag.Answers;
}
<form class="m-5" asp-area="" asp-controller="Test" asp-action="Vote" method="post">
@foreach (var question in questions)
{
<div class="mb-3">
<h5>@question.Content</h5>
@foreach (var answer in question.Answers)
{
<div class="form-check">
<input class="form-check-input" type="radio" name="[email protected]" id="@answer.Id" value="[email protected]">
<label class="form-check-label" for="@answer.Id">
@answer.Content
</label>
</div>
}
</div>
}
<button class="btn btn-primary w-100 py-2" type="submit">Отправить</button>
</form>
如何在控制器中获取这些值?
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StudyExamExample.Domain;
using StudyExamExample.Models;
namespace StudyExamExample.Controllers;
public class TestController(DataManager dataManager) : Controller
{
private readonly DataManager _dataManager = dataManager;
[Authorize]
// Roles = "Admin"
public async Task<IActionResult> Index()
{
ViewBag.Questions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
return View();
}
[Authorize]
public async Task<IActionResult> Vote(/*What there?*/)
{
return View();
}
}
我尝试用视图模型集合来实现,但不起作用 :(
namespace StudyExamExample.Models;
public class RadioButtonViewModel
{
public string GroupId { get; set; } = null!;
public string SelectedElementId { get; set; } = null!;
}
public class RadioButtonsSetViewModel
{
public IEnumerable<RadioButtonViewModel> Items { get; set; } = null!;
}
而且现在控制器(参数总是为空):
public class TestController(DataManager dataManager) : Controller
{
private readonly DataManager _dataManager = dataManager;
[Authorize]
// Roles = "Admin"
public async Task<IActionResult> Index()
{
ViewBag.Questions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
return View();
}
[Authorize]
public async Task<IActionResult> Vote(RadioButtonsSetViewModel radioButtons)
{
return View();
}
}
从浏览器发送的数据(忽略双破折号;在数据库中,ID为负数):
radio-group--5=option--15
radio-group--4=option--12
radio-group--3=option--8
radio-group--2=option--6
radio-group--1=option--3
解决方案
如果你想发布的请求体与 RadioButtonsSetViewModel 匹配,而不需要自定义逻辑来遍历 Request.Form 的值,表单结构应如下所示:
<form class="m-5" asp-area="" asp-controller="Test" asp-action="Vote" method="post">
@{
int itemId = 0;
foreach (var question in questions)
{
<div class="mb-3">
<h5>@question.Content</h5>
<input type="hidden" name="Items[@itemId].GroupId" id="Items[@itemId].GroupId" value="@question.Id" />
@foreach (var answer in question.Answers)
{
string answerFieldId = $"question_{question.Id}_{answer.Id}";
<div class="form-check">
<input class="form-check-input" type="radio" name="Items[@itemId].SelectedElementId" id="@answerFieldId" value="@answer.Id" />
<label class="form-check-label" for="@answerFieldId">
@answer.Content
</label>
</div>
}
</div>
itemId++;
}
<button class="btn btn-primary w-100 py-2" type="submit">Отправить</button>
}
</form>
变更如下:
- 通过对
itemId的迭代,在Items数组中构建RadioButtonViewModel对象。 - 增加一个隐藏输入,将
Items[@itemId].GroupId的值设为question.Id。 - 通过问题和答案的ID组合,使答案的
id唯一:question_{question.Id}_{answer.Id}。 - 将
Items[@itemId].SelectedElementId赋值为answer.Id。
因此,当提交表单时,请求体将与以下结构匹配:
Items[0].GroupId=5
Items[0].SelectedElementId=15
Items[1].GroupId=4
Items[1].SelectedElementId=12
Items[2].GroupId=3
Items[2].SelectedElementId=8
Items[3].GroupId=2
Items[3].SelectedElementId=6
Items[4].GroupId=1
Items[4].SelectedElementId=3
对于你的控制器动作:
public class TestController(DataManager dataManager) : Controller
{
...
[Authorize]
[HttpPost]
public async Task<IActionResult> Vote([FromForm] RadioButtonsSetViewModel model)
{
// Implementation for the Vote Action
}
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。
