2015-01-20 27 views
17

Rozpoczynam mój pierwszy projekt ASP.NET MVC, więc mam jedno proste pytanie. Mam następujący kod:ASP.NET MVC 5 grupa przycisków radiowych

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> 
} 

na wszystkie pytania w Model.GeneralQuestions są unikalne, więc przyciski radiowe powinny być podzielone na grupy według atrybutu name (dla każdego zapytania jednej grupy przycisków radiowych). Ale ten kod tworzy tylko jedną grupę, więc kiedy odpowiadam na drugie pytanie, pierwsza staje się odznaczona. Co muszę zmienić?

EDIT
Mój model wygląda następująco:

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; } 
} 

W jednej instancji StudentViewModel zapisać jeden uczeń i wszystkie pytania, na które powinien odpowiedzieć (zarówno ogólne jak i związane z przedmiotów studiuje) i przepuścić to zobaczyć. W związku z tym stawiam wszystkie pytania w jednej formie i wszystkie one są typem radia. Czy ktoś może mi pomóc w grupowaniu przycisków radiowych i prawidłowym przesyłaniu tego formularza?

+1

Tylko szybka odpowiedź, ale czy widziałeś ten artykuł? http://stackoverflow.com/a/22178728/1765853 – macoms01

+0

Tak długo jak 'QuestionString' jest unikalny, powinno to być tworzenie grupy dla każdego pytania, chociaż wiązanie z' QuestionString' wydaje się być niezmienne - nie powinieneś wiązać się z czymś podobnym 'SelectedAnswer'? Czy możesz pokazać trochę html, który generuje? –

+0

Twoja zewnętrzna pętla powinna być również pętlą 'for', tak aby twoje formanty były własnością o nazwie z indeksatorami, które mogą to wiązać po powrocie. –

Odpowiedz

23

Istnieje kilka problemów z tym generowanie kodu powielać id „s (nieprawidłowy HTML), generując powielać name atrybutów (który jest dlaczego tworzysz tylko jedną grupę, ale co ważniejsze, będzie to uniemożliwić wiązanie się model, gdy prześlesz z powrotem) i tak naprawdę nie jesteś w żaden sposób wiążący dla ważnej nieruchomości.

Musisz utworzyć modele widoku, aby pokazać, co chcesz wyświetlić i edytować oraz wygenerować przyciski radiowe w pętli for (lub przy użyciu EditorTemplate), aby zostały poprawnie nazwane za pomocą indeksatorów.

Zobacz modele

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; } 
} 

Zobacz

@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" /> 
} 

Controller

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 
} 

Uwaga jestem trochę zdezorientowany przez strukturę bazy domniemanych przez swoich modeli (na przykład dlaczego trzeba osobne modele dla Question i SubjectQuestion, gdy null wartość dla SubjectID określa ją jako pytanie "ogólne"). Sugeruję, aby zacząć od zwykłego kodowania niektórych wartości w metodzie GET, aby zobaczyć, jak działa i przesyła posty.

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 
    } 
    } 
}; 

Po wysłaniu model jest wypełniany identyfikatorem wybranej odpowiedzi dla każdego pytania w każdym temacie. Zwróć uwagę na użycie funkcji DisplayFor() dla niektórych właściwości. Nie będą one wysyłane z powrotem, więc będziesz musiał ponownie zaludnić te właściwości, jeśli zwrócisz widok (np. ModelState jest nieprawidłowy). Alternatywnie możesz wygenerować pole tekstowe tylko do odczytu lub dodać ukryte dane wejściowe dla tych właściwości. Ja również proponuję sprawdzić kod HTML, który jest generowany, w szczególności nazwa atrybuty, które będą wyglądać

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

dać zrozumienie, w jaki sposób kolekcje są zobowiązane do modelu na post Powrót

+0

Dziękuję! Wszystkie porady były pomocne, a kod jest poprawny. Teraz mam oddzielony model dla MVC i kupuję dla zapisu w bazie danych, ponieważ moje obiekty w bazie danych powinny wyglądać nieco inaczej niż model MVC. – bambi

+0

Czy istnieje sposób na przesyłanie zwrotne tekstów, nazw tematów itp. W obiekcie StudentVM? Dostaję tylko identyfikatory odpowiedzi na pytania w metodzie post ('[HttpPost] public ActionResult Student (StudentVM model) {}'), inne pola wydają się puste. – bambi

+0

Jak już zauważyłem, możesz utworzyć ukryte dane wejściowe dla tych właściwości w uzupełnieniu do '@ Html.DisplayFor()' - np. '@ Html.HiddenFor (m => m.Name)' lub możesz zastąpić 'DisplayFor' przez' @ Html.TextBoxFor (m => m.Name, new {@readonly = "readonly", @ class = "readonly ")', a następnie użyj css do stylu.Jednak tworzenie wielu niepotrzebnych ukrytych danych wejściowych może obniżyć wydajność (wysłanie go do klienta, a następnie z powrotem) i otwiera cię na atakowanie. Często lepiej jest po prostu ponownie pobrać dane z bazy danych, ale tylko Ty możesz ocenić, która opcja jest najlepsza. –

2

Sztuką jest użycie wyrażenia (pierwszy parametr do Html.RadioButtonFor), który zawiera wartość, która zmienia się na grupę przycisków radiowych.W twoim przypadku byłby to indeks na liście pytań.

Oto przykładowy kod:

@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 /> 
    } 
} 

To daje następujące 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 /> 

I Uzupełniająco o to zmniejszona wersja stosowanego modelu:

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; } 
} 

i tutaj jest kod z metody działania:

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"} 
        } 
      }, 
     } 
}); 
Powiązane problemy