使用剃刀在 ASP.NET MVC 页面中绑定单选按钮
Binding radio buttons in ASP.NET MVC page with razor
我有一个包含 4 个单选按钮的页面(试题和 4 个选项可供选择)。使用下面的代码,我可以在用户单击按钮后读取选项值。
我有两个问题:
- 为什么页面首次加载时所有单选按钮都被选中?
- 如何找出在post之后选择了哪个单选按钮?
谢谢!
StudentQuestions.cshtml
@page "{examId:int?}"
@model VerityLearn.WebUI.Pages.Questions.StudentQuestionsModel
@{
ViewData["Title"] = "StudentQuestions";
}
<div>
<form method="post">
@Html.LabelFor(model => model.QuestionViewModel.QuestionText);
<p>Select the correct option.</p>
int optionIndex = -1;
@foreach (OptionViewModel opt in Model.QuestionViewModel.Options)
{
optionIndex++;
@Html.RadioButtonFor(model => model.QuestionViewModel.Options[optionIndex], Model.QuestionViewModel.Options[optionIndex]);
@opt.OptionText<br />
}
<button type="submit" asp-route-questionIndex="@(Model.QuestionNdx + 1)" class="btn btn-primary @nextDisabled">Next</button>
</form>
</div>
StudentQuestions.cshtml.cs
public class StudentQuestionsModel : PageModel
{
//private readonly VerityContext _context;
private readonly SignInManager<VerityUser> _signInManager;
private readonly UserManager<VerityUser> _userManager;
private readonly IStudentQuesionsUOW _studentQuesionsUOW;
private readonly VerityLearn.DataAccess.VerityContext _context;
public StudentQuestionsModel(
VerityContext context,
SignInManager<VerityUser> signInManager,
UserManager<VerityUser> userMrg,
IStudentQuesionsUOW studentQuesionsUOW
)
{
_context = context;
_signInManager = signInManager;
_userManager = userMrg;
_studentQuesionsUOW = studentQuesionsUOW;
} // end public StudentQuestionsModel(VerityContext context, SignInManager<VerityUser> signInManager, UserManager<VerityUser> userMrg)
[BindProperty]
public QuestionViewModel QuestionViewModel { get; set; }
[BindProperty]
public Question Question { get; set; }
public async Task<IActionResult> OnPostAsync()
{
... Not sure what to do here ...
}
QuestionViewModel.cs
public class QuestionViewModel
{
public int QuestionId { get; set; }
public int ExamQuestionOrder { get; set; }
public string QuestionText { get; set; }
public bool? IsSingleSelection { get; set; }
public List<OptionViewModel> Options { get; set; }
}
如果您想使用单选按钮来表示互斥选项,您应该通过应用相同的 name
属性值将它们组合在一起:
<input type="radio" name="question1" value="answer1" /> Answer 1
<input type="radio" name="question1" value="answer2" /> Answer 2
<input type="radio" name="question1" value="answer3" /> Answer 3
您正在使用的 Html 助手为每个单选按钮生成不同的 id 和 name 属性值。我会避免在 Razor Pages 中使用 Html 助手来支持标签助手:
@for (var i = 0; i < Model.QuestionViewModel.Options.Count(); i++)
{
<input type="radio" asp-for="QuestionViewModel.QuestionId" value="@Model.QuestionViewModel.Options[i]" /> @Model.QuestionViewModel.Options[i].OptionText<br/>
}
更多信息:https://www.learnrazorpages.com/razor-pages/forms/radios
您感兴趣的是所选单选按钮的值。在我的演示中,为了让事情更简单,我假设它是一个 id。假设单选按钮的名称为 AnswerSelectedId
,然后在提交表单时将标有 AnswerSelectedId
的值回传到服务器。因此,您需要在名为 AnswerSelectedId
的模型中定义一个 属性,以便模型绑定可以匹配两者。
这里是一个快速演示,展示了您正在努力实现的目标,大量借鉴了 Mike Brind 的回答。
StudentQuestions.cshtml.cs
public class OptionViewModel
{
public int Id { get; set; }
public string OptionText { get; set; }
}
public class QuestionViewModel
{
public int QuestionId { get; set; }
public int ExamQuestionOrder { get; set; }
public string QuestionText { get; set; }
public bool? IsSingleSelection { get; set; }
public List<OptionViewModel> Options { get; set; }
}
public class StudentQuestionsModelModel : PageModel
{
public void OnGet()
{
QuestionViewModel = new QuestionViewModel
{
QuestionId = 1,
ExamQuestionOrder = 1,
QuestionText = "Question1",
IsSingleSelection = false,
Options = new List<OptionViewModel>()
{
new OptionViewModel
{
Id = 1,
OptionText = "Option 1"
},
new OptionViewModel
{
Id = 2,
OptionText = "Option 2"
}
}
};
}
[BindProperty]
public QuestionViewModel QuestionViewModel { get; set; }
[BindProperty]
public int AnswerSelectedId { get; set; } //this is the key bit
public async Task<IActionResult> OnPostAsync()
{
return Content($"The answer selected was {AnswerSelectedId}");
}
}
StudentQuestions.cshtml
@page "{examId:int?}"
@model StudentQuestions.Pages.StudentQuestionsModel
<div>
<form method="post">
@Html.LabelFor(model => model.QuestionViewModel.QuestionText);
<p>Select the correct option.</p>
@for (var i = 0; i < Model.QuestionViewModel.Options.Count(); i++)
{
<input type="radio" asp-for="AnswerSelectedId"
value="@Model.QuestionViewModel.Options[i].Id" />
@Model.QuestionViewModel.Options[i].OptionText
<br>
}
<button type="submit" class="btn btn-primary">Next</button>
</form>
</div>
请注意 asp-for 属性如何匹配页面模型中的 属性 AnswerSelectedId。
当您按下提交时,表单数据将被发送到服务器。如果您在发送到服务器的表单数据下使用开发人员工具,它将如下所示(取决于所选的值)
AnswerSelectedId: 1
然后模型绑定会将值绑定到您的 属性 名为 AnswerSelectedId 的值,然后您将能够在 onPostAsync 方法中访问它。
我建议使用 Animated radios & checkboxes (noJS),它的动画效果非常好。您可以按照以下步骤在 Razor 页面中使用它:
css:
.checkbox label:after, .radio label:after {
content: '';
display: table;
clear: both;
}
.checkbox .cr, .radio .cr {
position: relative;
display: inline-block;
border: 1px solid #a9a9a9;
border-radius: .25em;
width: 1.3em;
height: 1.3em;
float: left;
margin-right: .5em;
}
.radio .cr {
border-radius: 50%;
}
.checkbox .cr .cr-icon, .radio .cr .cr-icon {
position: absolute;
font-size: .8em;
line-height: 0;
top: 50%;
left: 20%;
}
.radio .cr .cr-icon {
margin-left: 0.04em;
}
.checkbox label input[type="checkbox"], .radio label input[type="radio"] {
display: none;
}
.checkbox label input[type="checkbox"] + .cr > .cr-icon, .radio label input[type="radio"] + .cr > .cr-icon {
transform: scale(3) rotateZ(-20deg);
opacity: 0;
transition: all .3s ease-in;
}
.checkbox label input[type="checkbox"]:checked + .cr > .cr-icon, .radio label input[type="radio"]:checked + .cr > .cr-icon {
transform: scale(1) rotateZ(0deg);
opacity: 1;
}
.checkbox label input[type="checkbox"]:disabled + .cr, .radio label input[type="radio"]:disabled + .cr {
opacity: .5;
}
控制器:
public async Task<ActionResult> Edit(int id)
{
//code omitted for brevity
return PartialView("_Edit", model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Update([Bind(Exclude = null)] GroupViewModel model,
params string[] selectedRoles)
{
//code omitted for brevity
await this.GroupManager.UpdateGroupAsync(group);
selectedRoles = selectedRoles ?? new string[] { };
await this.GroupManager.SetGroupRolesAsync(group.Id, selectedRoles);
}
查看:
@model Demo.Models.GroupViewModel
<div class="form-group">
@Html.Label("Roles", new { @class = "col-md-3 control-label" })
<div class="col-md-6">
<div class="checkbox">
<label>
<input type="checkbox" id="checkAll" name="" class="" />
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span>
<text id= "checkText">Tümü?</text>
</label>
</div>
@foreach (var role in Model.RolesList)
{
<div class="checkbox">
<label>
<input type="checkbox" name="selectedRoles" value="@role.Text" checked="@role.Selected" class="" />
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span>
@role.Text
</label>
</div>
}
</div>
</div>
<script>
// for selecting All / None
$("#checkAll").change(function () {
var checked = $(this).prop("checked");
$("input:checkbox").prop('checked', checked);
$('#checkText').text(checked ? 'None?' : 'All?');
});
</script>
希望对您有所帮助。
我有一个包含 4 个单选按钮的页面(试题和 4 个选项可供选择)。使用下面的代码,我可以在用户单击按钮后读取选项值。
我有两个问题:
- 为什么页面首次加载时所有单选按钮都被选中?
- 如何找出在post之后选择了哪个单选按钮?
谢谢!
StudentQuestions.cshtml
@page "{examId:int?}"
@model VerityLearn.WebUI.Pages.Questions.StudentQuestionsModel
@{
ViewData["Title"] = "StudentQuestions";
}
<div>
<form method="post">
@Html.LabelFor(model => model.QuestionViewModel.QuestionText);
<p>Select the correct option.</p>
int optionIndex = -1;
@foreach (OptionViewModel opt in Model.QuestionViewModel.Options)
{
optionIndex++;
@Html.RadioButtonFor(model => model.QuestionViewModel.Options[optionIndex], Model.QuestionViewModel.Options[optionIndex]);
@opt.OptionText<br />
}
<button type="submit" asp-route-questionIndex="@(Model.QuestionNdx + 1)" class="btn btn-primary @nextDisabled">Next</button>
</form>
</div>
StudentQuestions.cshtml.cs
public class StudentQuestionsModel : PageModel
{
//private readonly VerityContext _context;
private readonly SignInManager<VerityUser> _signInManager;
private readonly UserManager<VerityUser> _userManager;
private readonly IStudentQuesionsUOW _studentQuesionsUOW;
private readonly VerityLearn.DataAccess.VerityContext _context;
public StudentQuestionsModel(
VerityContext context,
SignInManager<VerityUser> signInManager,
UserManager<VerityUser> userMrg,
IStudentQuesionsUOW studentQuesionsUOW
)
{
_context = context;
_signInManager = signInManager;
_userManager = userMrg;
_studentQuesionsUOW = studentQuesionsUOW;
} // end public StudentQuestionsModel(VerityContext context, SignInManager<VerityUser> signInManager, UserManager<VerityUser> userMrg)
[BindProperty]
public QuestionViewModel QuestionViewModel { get; set; }
[BindProperty]
public Question Question { get; set; }
public async Task<IActionResult> OnPostAsync()
{
... Not sure what to do here ...
}
QuestionViewModel.cs
public class QuestionViewModel
{
public int QuestionId { get; set; }
public int ExamQuestionOrder { get; set; }
public string QuestionText { get; set; }
public bool? IsSingleSelection { get; set; }
public List<OptionViewModel> Options { get; set; }
}
如果您想使用单选按钮来表示互斥选项,您应该通过应用相同的 name
属性值将它们组合在一起:
<input type="radio" name="question1" value="answer1" /> Answer 1
<input type="radio" name="question1" value="answer2" /> Answer 2
<input type="radio" name="question1" value="answer3" /> Answer 3
您正在使用的 Html 助手为每个单选按钮生成不同的 id 和 name 属性值。我会避免在 Razor Pages 中使用 Html 助手来支持标签助手:
@for (var i = 0; i < Model.QuestionViewModel.Options.Count(); i++)
{
<input type="radio" asp-for="QuestionViewModel.QuestionId" value="@Model.QuestionViewModel.Options[i]" /> @Model.QuestionViewModel.Options[i].OptionText<br/>
}
更多信息:https://www.learnrazorpages.com/razor-pages/forms/radios
您感兴趣的是所选单选按钮的值。在我的演示中,为了让事情更简单,我假设它是一个 id。假设单选按钮的名称为 AnswerSelectedId
,然后在提交表单时将标有 AnswerSelectedId
的值回传到服务器。因此,您需要在名为 AnswerSelectedId
的模型中定义一个 属性,以便模型绑定可以匹配两者。
这里是一个快速演示,展示了您正在努力实现的目标,大量借鉴了 Mike Brind 的回答。
StudentQuestions.cshtml.cs
public class OptionViewModel
{
public int Id { get; set; }
public string OptionText { get; set; }
}
public class QuestionViewModel
{
public int QuestionId { get; set; }
public int ExamQuestionOrder { get; set; }
public string QuestionText { get; set; }
public bool? IsSingleSelection { get; set; }
public List<OptionViewModel> Options { get; set; }
}
public class StudentQuestionsModelModel : PageModel
{
public void OnGet()
{
QuestionViewModel = new QuestionViewModel
{
QuestionId = 1,
ExamQuestionOrder = 1,
QuestionText = "Question1",
IsSingleSelection = false,
Options = new List<OptionViewModel>()
{
new OptionViewModel
{
Id = 1,
OptionText = "Option 1"
},
new OptionViewModel
{
Id = 2,
OptionText = "Option 2"
}
}
};
}
[BindProperty]
public QuestionViewModel QuestionViewModel { get; set; }
[BindProperty]
public int AnswerSelectedId { get; set; } //this is the key bit
public async Task<IActionResult> OnPostAsync()
{
return Content($"The answer selected was {AnswerSelectedId}");
}
}
StudentQuestions.cshtml
@page "{examId:int?}"
@model StudentQuestions.Pages.StudentQuestionsModel
<div>
<form method="post">
@Html.LabelFor(model => model.QuestionViewModel.QuestionText);
<p>Select the correct option.</p>
@for (var i = 0; i < Model.QuestionViewModel.Options.Count(); i++)
{
<input type="radio" asp-for="AnswerSelectedId"
value="@Model.QuestionViewModel.Options[i].Id" />
@Model.QuestionViewModel.Options[i].OptionText
<br>
}
<button type="submit" class="btn btn-primary">Next</button>
</form>
</div>
请注意 asp-for 属性如何匹配页面模型中的 属性 AnswerSelectedId。
当您按下提交时,表单数据将被发送到服务器。如果您在发送到服务器的表单数据下使用开发人员工具,它将如下所示(取决于所选的值)
AnswerSelectedId: 1
然后模型绑定会将值绑定到您的 属性 名为 AnswerSelectedId 的值,然后您将能够在 onPostAsync 方法中访问它。
我建议使用 Animated radios & checkboxes (noJS),它的动画效果非常好。您可以按照以下步骤在 Razor 页面中使用它:
css:
.checkbox label:after, .radio label:after {
content: '';
display: table;
clear: both;
}
.checkbox .cr, .radio .cr {
position: relative;
display: inline-block;
border: 1px solid #a9a9a9;
border-radius: .25em;
width: 1.3em;
height: 1.3em;
float: left;
margin-right: .5em;
}
.radio .cr {
border-radius: 50%;
}
.checkbox .cr .cr-icon, .radio .cr .cr-icon {
position: absolute;
font-size: .8em;
line-height: 0;
top: 50%;
left: 20%;
}
.radio .cr .cr-icon {
margin-left: 0.04em;
}
.checkbox label input[type="checkbox"], .radio label input[type="radio"] {
display: none;
}
.checkbox label input[type="checkbox"] + .cr > .cr-icon, .radio label input[type="radio"] + .cr > .cr-icon {
transform: scale(3) rotateZ(-20deg);
opacity: 0;
transition: all .3s ease-in;
}
.checkbox label input[type="checkbox"]:checked + .cr > .cr-icon, .radio label input[type="radio"]:checked + .cr > .cr-icon {
transform: scale(1) rotateZ(0deg);
opacity: 1;
}
.checkbox label input[type="checkbox"]:disabled + .cr, .radio label input[type="radio"]:disabled + .cr {
opacity: .5;
}
控制器:
public async Task<ActionResult> Edit(int id)
{
//code omitted for brevity
return PartialView("_Edit", model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Update([Bind(Exclude = null)] GroupViewModel model,
params string[] selectedRoles)
{
//code omitted for brevity
await this.GroupManager.UpdateGroupAsync(group);
selectedRoles = selectedRoles ?? new string[] { };
await this.GroupManager.SetGroupRolesAsync(group.Id, selectedRoles);
}
查看:
@model Demo.Models.GroupViewModel
<div class="form-group">
@Html.Label("Roles", new { @class = "col-md-3 control-label" })
<div class="col-md-6">
<div class="checkbox">
<label>
<input type="checkbox" id="checkAll" name="" class="" />
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span>
<text id= "checkText">Tümü?</text>
</label>
</div>
@foreach (var role in Model.RolesList)
{
<div class="checkbox">
<label>
<input type="checkbox" name="selectedRoles" value="@role.Text" checked="@role.Selected" class="" />
<span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span>
@role.Text
</label>
</div>
}
</div>
</div>
<script>
// for selecting All / None
$("#checkAll").change(function () {
var checked = $(this).prop("checked");
$("input:checkbox").prop('checked', checked);
$('#checkText').text(checked ? 'None?' : 'All?');
});
</script>
希望对您有所帮助。