Reputation: 5
I am currently having an issue with trying to post information from multiple choice answer options via radio buttons using razor pages and ASP.net Core. I've found some info for MVC but can't quite fix my issue with it.
I have a Razor page that displays a list of questions and, within each question its possible answer options. This is my form below:
<form method="post">
@for (int i = 0; i < Model.Questions.Count(); i++)
{
<h4>@Model.Questions.ElementAt(i).QuestionBody</h4>
<input type="hidden" asp-for="Question.QuestionBody" />
@foreach (var answer in Model.Answers)
{
var groupID = "question" + i;
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="radio" name="@groupID" />
<label>answer.AnswerBody</label>
</div>
</div>
</div>
}
}
<button type="submit" class="btn btn-primary">Save</button>
</form>
From info I've read I think I need to remove the name attribute but currently it is what is grouping my answers to the correct questions and allowing me to select multiple answers per page.
The Page Model for the ApTest razor page which contains the form is
public class ApTestModel : PageModel
{
private readonly IConfiguration config;
public IApTestData ApTestData { get; set; }
[BindProperty]
public IEnumerable<Question> Questions { get; set; }
[BindProperty]
public Question Question { get; set; }
public int QuestionID { get; set; }
[BindProperty]
public List<Answer> Answers { get; set; }
[BindProperty]
public Answer Answer { get; set; }
[BindProperty]
public TestAttempt TestAttempt { get; set; }
public ApTestModel(IConfiguration config, IApTestData ApTestData)
{
this.config = config;
this.ApTestData = ApTestData;
}
public void OnGet()
{
Questions = ApTestData.GetQuestionsAndAnswers(QuestionID);
}
public void OnPost()
{
AppUser appUser = new AppUser
{
Email = User.Identity.Name,
};
ApTestData.SaveApTestAttempt(Answers, appUser);
}
}
}
The SaveApTestAttempt method is here:
public void SaveApTestAttempt(List<Answer> answers, AppUser appUser)
{
var tempAppUser = db.Users.SingleOrDefault(user => user.Email == appUser.Email);
foreach (var answer in answers)
{
TestAttempt testAttempt = new TestAttempt
{
SelctedAnswer = answer,
Applicant = appUser,
ApplicantID = tempAppUser.Id
};
db.TestAttempts.Add(testAttempt);
}
}
and the entities involved are Question, Answer, and TestAttempt (All seperate classes but combined here for easy viewing):
public class Question
{
public int ID { get; set; }
public string QuestionBody { get; set; }
public List<Answer> Answers { get; set; }
public QuestionTypeID QuestionTypeID { get; set; }
public QuestionType QuestionType { get; set; }
public Question()
{
Answers = new List<Answer>();
}
}
public class Answer
{
public int ID { get; set; }
public string AnswerBody { get; set; }
public bool IsCorrect { get; set; }
public Question Question { get; set; }
public int QuestionID { get; set; }
}
public class TestAttempt
{
[Key]
public int ID { get; set; }
public AppUser Applicant { get; set; }
public string ApplicantID { get; set; }
public Answer SelctedAnswer { get; set; }
public int AnswerID { get; set; }
}
I know I should be binding and using asp-for within my form but I have tried that as well as removing the name attribute and adding a value but I don't think I am ever adding the correct thing. I was using something like Model.Questions.ElementAt(i).Answers[j].AnswerID
Still fairly new to the ASP.Net Core world and working on my dissertation project for uni, Any help would be greatly appreciated!!
Upvotes: 0
Views: 2244
Reputation: 36765
Model binding looks through the sources for the name pattern prefix.property_name
. If nothing is found, it looks for just property_name
without the prefix.What you define for @groupID
cannot be bound to the model due to the name does not match to the property name.
And your code have another error is that you do not define Answers
in the handler but you loop it in the razor pages,it is impossible.
public class ApTestModel : PageModel
{
private readonly IConfiguration config;
[BindProperty]
public IEnumerable<Question> Questions { get; set; }
[BindProperty]
public Question Question { get; set; }
public int QuestionID { get; set; }
[BindProperty]
public List<Answer> Answers { get; set; }
[BindProperty]
public Answer Answer { get; set; }
[BindProperty]
public TestAttempt TestAttempt { get; set; }
public PrivacyModel(IConfiguration config)
{
this.config = config;
}
public void OnGet()
{
Questions = new List<Question>()
{
new Question(){
ID=1,
QuestionBody="question1"
},
new Question(){
ID=2,
QuestionBody="question2"
},
new Question(){
ID=3,
QuestionBody="question3"
}
};
Answers = new List<Answer>(){
new Answer() {ID=1, AnswerBody="an1", QuestionID=1} ,
new Answer() {ID=2, AnswerBody="an2", QuestionID=1} ,
new Answer() {ID=3, AnswerBody="an3", QuestionID=1}
};
}
public void OnPost()
{
AppUser appUser = new AppUser
{
Email = "aaaa",
};
SaveApTestAttempt(Answers, appUser);
}
public void SaveApTestAttempt(List<Answer> answers, AppUser appUser)
{
//...
}
}
Change your razor pages like below(Not sure which value in Answer class you want to bind to the radio button.You could change the name by yourself):
<form method="post">
@for (int i = 0; i < Model.Questions.Count(); i++)
{
<h4>@Model.Questions.ElementAt(i).QuestionBody</h4>
<input type="hidden" name="[@i].QuestionBody" asp-for="@Model.Questions.ElementAt(i).QuestionBody" />
@foreach (var answer in Model.Answers)
{
var groupID = "[" + i + "].AnswerBody";
<input type="hidden" asp-for="@answer.ID" />
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<input type="radio" name="@groupID" value="@answer.AnswerBody" />
<label>@answer.AnswerBody</label>
</div>
</div>
</div>
}
}
<button type="submit" class="btn btn-primary">Save</button>
</form>
Result:
Upvotes: 1