使用剃刀在 ASP.NET MVC 页面中绑定单选按钮

Binding radio buttons in ASP.NET MVC page with razor

我有一个包含 4 个单选按钮的页面(试题和 4 个选项可供选择)。使用下面的代码,我可以在用户单击按钮后读取选项值。

我有两个问题:

谢谢!

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>

希望对您有所帮助。