Grupo de botões de rádio da ASP.NET MVC 5

Eu estou começando meu primeiro projeto ASP.NET MVC, então eu tenho uma pergunta simples. Eu tenho o seguinte código:

foreach(var question in Model.GeneralQuestions) { 

@question.QuestionString

@foreach (var answer in question.PossibleAnswers) { @Html.RadioButtonFor(model => question.QuestionString, answer.Answer) @Html.Label(answer.Answer)
}
}

Todas as perguntas em Model.GeneralQuestions são exclusivas, portanto, os botões de opção devem ser divididos em grupos por atributo de nome (para cada pergunta, um grupo de botões de opção). Mas esse código produz apenas um grupo, então quando eu respondo à segunda pergunta, primeiro, um é desmarcado. O que eu preciso mudar?

EDITAR
Meu modelo parece:

 public class StudentViewModel { public Student Student { get; set; } public List GeneralQuestions { get; set; } public List SubjectQuestions { get; set; } } public class Student { public int StudentID { get; set; } public string Index { get; set; } public string Name { get; set; } public string Surname { get; set; } public virtual ICollection Subjects { get; set; } } public class Question { public int QuestionID { get; set; } public string QuestionString { get; set; } public bool IsAssociatedWithSubject { get; set; } public virtual ICollection PossibleAnswers { get; set; } public virtual ICollection Results { get; set; } } public class SubjectQuestions { public Subject Subject { get; set; } public List Questions { get; set; } } public class Results { public int ResultsID { get; set; } public int QuestionID { get; set; } public int? SubjectID { get; set; } public int PossibleAnswerID { get; set; } public virtual Question Question { get; set; } public virtual PossibleAnswer PossibleAnswer { get; set; } public virtual Subject Subject { get; set; } } 

Em uma instância de StudentViewModel eu salvo um aluno e todas as perguntas que ele deveria responder (gerais e relacionadas aos assuntos que ele está estudando) e passe para ver. Em vista, coloco todas as questões de forma única e elas são todo tipo de rádio. Então, alguém pode me ajudar com o agrupamento de botões de rádio e postar de volta este formulário corretamente?

Há um número de problemas com o seu código, incluindo a geração de id duplicados (html inválido), gerando atributos de name duplicados (é por isso que você está criando apenas um grupo, mas o mais importante é evitar a vinculação ao modelo quando post back) e você não está realmente vinculado a uma propriedade válida de qualquer maneira.

Você precisará criar modelos de exibição para representar o que deseja exibir e editar e gerar os botões de opção em um loop for (ou usando um EditorTemplate ) para que eles sejam nomeados corretamente com indexadores.

Visualizar modelos

 public class QuestionVM { public int ID { get; set; } // for binding public string Text { get; set; } [Required] public int? SelectedAnswer { get; set; } // for binding public IEnumerable PossibleAnswers { get; set; } } public class SubjectVM { public int? ID { get; set; } [DisplayFormat(NullDisplayText = "General")] public string Name { get; set; } public List Questions { get; set; } } public class AnswerVM { public int ID { get; set; } public string Text { get; set; } } public class StudentVM { public int ID { get; set; } public string Name { get; set; } // plus any other properties of student that you want to display in the view public List Subjects { get; set; } } 

Visão

 @model YourAssembly.StudentVM @using(Html.BeginForm()) { @Html.HiddenFor(m => m.ID) @Html.DisplayFor(m => m.Name) for(int i = 0; i < Model.Subjects.Count; i++) { @Html.HiddenFor(m => m.Subjects[i].ID) @Html.DisplayFor(m => m.Subjects[i].Name) // will display "General" if no name for (int j = 0; j < Model.Subjects[i].Questions.Count; j++) { @Html.HiddenFor(m => m.Subjects[i].Questions[j].ID) @Html.DisplayFor(m => m.Subjects[i].Questions[j].Text) foreach(var answer in Model.Subjects[i].Questions[j].PossibleAnswers ) { 
@Html.RadioButtonFor(m => m.Subjects[i].Questions[j].SelectedAnswer, answer.ID, new { id = answer.ID})
} @Html.ValidationMessageFor(m => m.Subjects[i].Questions[j].SelectedAnswer) } } }

Controlador

 public ActionResult Edit(int ID) { StudentVM model = new StudentVM(); // populate your view model with values from the database return View(model); } [HttpPost] public ActionResult Edit(StudentVM model) { // save and redirect } 

Observe que estou um pouco confuso com a estrutura do database implícita em seus modelos (por exemplo, por que você precisa de modelos separados para Question e SubjectQuestion quando um valor null para SubjectID identifica como uma questão “Geral”). Eu sugiro que você inicie apenas codificando alguns valores no método GET para ver como ele funciona e postar de volta.

 StudentVM model = new StudentVM(); model.ID = 1; model.Name = "bambiinela"; model.Subjects = new List() { new SubjectVM() { Questions = new List() { new QuestionVM() { ID = 1, Text = "Question 1", SelectedAnswer = ?, // set this if you want to preselect an option PossibleAnswers = new List() { new AnswerVM() { ID = 1, Text = "Answer A" }, new AnswerVM() { ID = 1, Text = "Answer B" } } }, new QuestionVM() { ID = 2, Text = "Question 2", PossibleAnswers = new List() { // similar to above } } } }, new SubjectVM() { ID = 1, Name = "Math", Questions = new List() { // similar to above } } }; 

Quando você publica, o modelo é preenchido com o ID da resposta selecionada para cada pergunta em cada assunto. Observe o uso de DisplayFor() para algumas propriedades. Estas não serão postadas de volta, então você precisaria preencher essas propriedades novamente se retornar a visualização (por exemplo, ModelState não é válido). Como alternativa, você pode gerar uma checkbox de texto somente leitura ou adicionar uma input oculta para essas propriedades. Eu também sugiro que você inspecione o HTML que é gerado, em particular os atributos de nome que parecerão algo como

  

para lhe dar uma compreensão de como as collections são vinculadas ao seu modelo no pós-back

O truque é usar uma expressão (primeiro parâmetro para Html.RadioButtonFor) que contém um valor que muda por grupo de botões de rádio. No seu caso, seria um índice na lista de perguntas.

Aqui está um código de amostra:

  @for (int i = 0; i < Model.GeneralQuestions.Count; i++) { var question = Model.GeneralQuestions[i]; @Html.Label(question.QuestionString) 
foreach (var answer in question.PossibleAnswers) { @Html.RadioButtonFor(model => Model.GeneralQuestions[i].SelectedAnswerId, answer.Id) @Html.Label(answer.Answer)
} }

Isso produz o seguinte HTML:

  





E para completar, aqui está uma versão reduzida dos modelos usados:

 public class StudentViewModel { public List GeneralQuestions { get; set; } } public class Question { public int QuestionId { get; set; } public string QuestionString { get; set; } public ICollection PossibleAnswers { get; set; } public int SelectedAnswerId { get; set; } } public class PossibleAnswer { public int Id { get; set; } public string Answer { get; set; } } 

e aqui está o código do método de ação:

 return View(new StudentViewModel { GeneralQuestions = new List { new Question { QuestionString = "Q1", PossibleAnswers = new[] { new PossibleAnswer {Id = 1, Answer = "A01"}, new PossibleAnswer {Id = 2, Answer = "A02"} } }, new Question { QuestionString = "Q2", PossibleAnswers = new[] { new PossibleAnswer {Id = 11, Answer = "A11"}, new PossibleAnswer {Id = 12, Answer = "A12"} } }, } });