2015-01-20 90 views
17

我开始了我的第一个ASP.NET MVC项目,所以我有一个简单的问题。 我有以下代码:ASP.NET MVC 5组单选按钮

foreach(var question in Model.GeneralQuestions) 
{ 
    <div class = "well"> 
     <h3> 
      <strong>@question.QuestionString</strong> 
     </h3> 
     @foreach (var answer in question.PossibleAnswers) 
     { 
      @Html.RadioButtonFor(model => question.QuestionString, answer.Answer) 
      @Html.Label(answer.Answer) 
      <br /> 
     } 
    </div> 
} 

在Model.GeneralQuestions所有问题都是独一无二的,所以单选按钮应该由name属性被分成组(每题一组单选按钮)。但是这个代码只产生一个组,所以当我第一个回答第二个问题时就会取消选择。 我需要改变什么?

编辑
我的模型看起来像:

public class StudentViewModel 
{ 
    public Student Student { get; set; } 
    public List<Question> GeneralQuestions { get; set; } 
    public List<SubjectQuestions> 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<Subject> Subjects { get; set; } 
} 
public class Question 
{ 
    public int QuestionID { get; set; } 
    public string QuestionString { get; set; } 
    public bool IsAssociatedWithSubject { get; set; } 

    public virtual ICollection<PossibleAnswer> PossibleAnswers { get; set; } 
    public virtual ICollection<Results> Results { get; set; } 
} 
public class SubjectQuestions 
{ 
    public Subject Subject { get; set; } 
    public List<Question> 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; } 
} 

在StudentViewModel的一个实例我救一个学生,所有的问题,他应该回答(一般和相关学科他是学)和通它来查看。鉴于我将所有问题都以单一形式提出,并且都是无线电广播。所以,任何人都可以帮助我分组单选按钮并正确回复此表单吗?

+1

只是一个快速反应,但你见过这篇文章吗? http://stackoverflow.com/a/22178728/1765853 – macoms01 2015-01-20 21:22:27

+0

只要'QuestionString'是唯一的,这应该是为每个问题创建一个组,虽然绑定到'QuestionString'似乎是固定的 - 你不应该绑定到类似的东西'SelectedAnswer'?你可以显示一些这是生成的HTML – 2015-01-20 21:27:39

+0

你的外循环也应该是一个'for'循环,这样你的控件属性可以通过索引器命名。 – 2015-01-20 21:30:50

回答

23

您的代码有许多问题,包括生成重复的id的(无效的html),生成重复的name属性(这就是为什么您只创建一个组,但更重要的是,这会阻止您绑定到当你回发)时,你实际上并没有绑定到有效的属性。

您需要创建视图模型来表示要在for循环(或使用EditorTemplate)中显示和编辑并生成单选按钮,以便使用索引器正确命名它们。

查看模型

@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) 
     { 
     <div> 
      @Html.RadioButtonFor(m => m.Subjects[i].Questions[j].SelectedAnswer, answer.ID, new { id = answer.ID}) 
      <label for="@answer.ID">@answer.Text</label> 
     </div> 
     } 
     @Html.ValidationMessageFor(m => m.Subjects[i].Questions[j].SelectedAnswer) 
    } 
    } 
    <input type="submit" value="save" /> 
} 

控制器

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 
} 

注意我有点困惑,通过你的模型所隐含的数据库结构(例如你为什么需要

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<AnswerVM> PossibleAnswers { get; set; } 
} 

public class SubjectVM 
{ 
    public int? ID { get; set; } 
    [DisplayFormat(NullDisplayText = "General")] 
    public string Name { get; set; } 
    public List<QuestionVM> 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<SubjectVM> Subjects { get; set; } 
} 

查看QuestionSubjectQuestion的单独型号为null值为SubjectID将其标识为“常规”问题)。我建议你首先在GET方法中对某些值进行硬编码,以查看它是如何工作并回发的。

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

当您发布时,模型会填入每个主题中每个问题的选定答案的ID。请注意,某些属性使用DisplayFor()。这些将不会回发,因此如果您返回视图,则需要重新填充这些属性(例如ModelState无效)。或者,您可以为这些属性生成只读文本框或添加隐藏输入。我也建议你检查所生成的HTML,尤其是name属性,这将是这个样子

<input type="radio" name="Subjects[0].Questions[0].SelectedAnswer" ... 

给你的藏品是如何绑定到模型上回发

+0

谢谢!所有的建议都很有帮助,代码是正确的。现在我已经分离了MVC的模型,并将其保存在数据库中,因为我在数据库中的实体应该看起来与MVC模型有点不同。 – bambi 2015-01-21 15:02:22

+0

是否有任何方式在StudentVM对象中发布回问题文本,主题名称等?我所得到的是post方法中回答问题的ID('[HttpPost] public ActionResult Student(StudentVM model) {}'),其他字段似乎是空的。 – bambi 2015-01-22 00:28:35

+0

正如我所指出的那样,除了'@ Html.DisplayFor()'之外,您还可以为这些属性创建隐藏的输入。 '@ H​​tml.HiddenFor(m => m.Name)''或者你可以用'@ Html.TextBoxFor(m => m.Name,new {@readonly =“readonly”,@ class =“readonly “)'然后用css来设置它。然而,创建大量不必要的隐藏输入可能会降低性能(将其发送给客户端,然后再返回),并向您提供重复发布攻击的机会。通常情况下,再次从数据库中获取数据会更好,但只有您可以评估最佳选择。 – 2015-01-22 00:51:50

2

诀窍的理解是使用一个表达式(Html.RadioButtonFor的第一个参数),其中包含一个值,每个单选按钮组都会改变一个值。就你而言,它将成为问题列表中的一个索引。

下面是一些示例代码:

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

这将产生以下HTML:

<label for="Q1">Q1</label> 
<br /> 
<input id="GeneralQuestions_0__SelectedAnswerId" 
    name="GeneralQuestions[0].SelectedAnswerId" type="radio" value="1" /> 
<label for="A01">A01</label> 
<br /> 
<input id="GeneralQuestions_0__SelectedAnswerId" 
    name="GeneralQuestions[0].SelectedAnswerId" type="radio" value="2" /> 
<label for="A02">A02</label> 
<br /> 
<label for="Q2">Q2</label> 
<br /> 
<input id="GeneralQuestions_1__SelectedAnswerId" 
    name="GeneralQuestions[1].SelectedAnswerId" type="radio" value="11" /> 
<label for="A11">A11</label> 
<br /> 
<input id="GeneralQuestions_1__SelectedAnswerId" 
    name="GeneralQuestions[1].SelectedAnswerId" type="radio" value="12" /> 
<label for="A12">A12</label> 
<br /> 

而且为了完整起见,这里是模型的简化版本中使用:

public class StudentViewModel 
{ 
    public List<Question> GeneralQuestions { get; set; } 
} 

public class Question 
{ 
    public int QuestionId { get; set; } 
    public string QuestionString { get; set; } 
    public ICollection<PossibleAnswer> PossibleAnswers { get; set; } 
    public int SelectedAnswerId { get; set; } 
} 

public class PossibleAnswer 
{ 
    public int Id { get; set; } 
    public string Answer { get; set; } 
} 

这里是来自动作方法的代码:

return View(new StudentViewModel 
{ 
    GeneralQuestions = 
     new List<Question> 
     { 
      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"} 
        } 
      }, 
     } 
});