2015-03-31 4 views
2

У меня есть две модели, вопрос и ответ. Я хочу вставить список ответов через ViewModel в вопрос, но, похоже, в моем методе post мой список получает null. Это может быть и плохая реализация, потому что я возвращаю модель своего вопроса обратно, когда я отправляю что-то, и я думаю, что мой список просто получает нуль. Как я могу это исправить?Почему мой список ViewData null? MVC 4

Редактировать: Я переделал контроллер и представление, основываясь на комментариях, которые вы мне дали: То, как это выглядит сейчас, но кажется, что мой список ответов снова пуст.

ViewModel:

public class ViewModel 
{ 
    public IEnumerable<Answer> Answers { get; set; } 
    public Question Question { get; set; } 
} 

Контроллер:

[Authorize] 
     public ActionResult Create() 
     { 
      ViewModel vm = new ViewModel(); 
      ViewBag.BelongToTest = new SelectList(db.Tests, "TestId" , "TestTitle").FirstOrDefault(); 
      vm.Question = new Question { Question_Text = String.Empty }; 
      vm.Answers = new List<Answer> { new Answer { CorrectOrNot = false, AnswerText = "", OpenAnswerText = "" } }; 
      return View(vm); 
     } 

     // 
     // POST: /Question/Create 

     [HttpPost] 
     [Authorize] 
     public ActionResult Create(ViewModel vm) 
     { 

       if (ModelState.IsValid) 
       { 

        vm.Question.BelongToTest = (from t in db.Tests 
              join m in db.Members on t.AddedByUser equals m.MemberId 
              where m.UserID == WebSecurity.CurrentUserId && 
              t.AddedByUser == m.MemberId 
              orderby t.TestId descending 
              select t.TestId).FirstOrDefault(); 

        db.Questions.Add(vm.Question); 
        db.SaveChanges(); 

        if (vm.Answers != null) 
        { 
         foreach (var i in vm.Answers) 
         { 
          i.BelongToQuestion = vm.Question.QuestionId; 

          db.Answers.Add(i); 
         } 
        } 

        db.SaveChanges(); 
        ViewBag.Message = "Data successfully saved!"; 
        ModelState.Clear(); 

       } 

       ViewBag.BelongToTest = new SelectList(db.Tests, "TestId", "TestTitle", vm.Question.BelongToTest); 
       vm.Question = new Question { Question_Text = String.Empty }; 
       vm.Answers = new List<Answer> { new Answer { CorrectOrNot = false, AnswerText = "", OpenAnswerText = "" } }; 
       return View("Create" , vm); 

     } 

Вид:

@model MvcTestApplication.Models.ViewModel 
@using MvcTestApplication.Models 

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> 

@{ 
    ViewBag.Title = "Create"; 
} 

@using (Html.BeginForm("Create", "Question", FormMethod.Post)) { 

<h2>Create</h2> 

<table> 
    <tr> 
     <th>Question Name</th> 
    </tr> 

     <tr> 
      <td>@Html.EditorFor(model=>model.Question.Question_Text)</td> 
     </tr> 

</table> 

<table id="dataTable"> 
    <tr> 
     <th>Correct?</th> 
     <th>Answer text</th> 
     <th>Open Answer</th> 
    </tr> 
    @foreach(var i in Model.Answers) 
{ 
    <tr> 
     <td>@Html.CheckBoxFor(model=>i.CorrectOrNot)</td> 
     <td>@Html.EditorFor(model=>i.AnswerText)</td> 
     <td>@Html.EditorFor(model=>i.OpenAnswerText)</td> 
    </tr> 
} 
</table> 

<input type="button" id="addNew" value="Add Answer"/> 
<input type="submit" value="Create" /> 

} 

@section Scripts { 
    @Scripts.Render("~/bundles/jqueryval") 

    <script lang="javascript"> 
     $(document).ready(function() { 

      //1. Add new row 
      $("#addNew").click(function (e) { 
       e.preventDefault(); 
       var $tableBody = $("#dataTable"); 
       var $trLast = $tableBody.find("tr:last"); 
       var $trNew = $trLast.clone(); 

       var suffix = $trNew.find(':input:first').attr('name').match(/\d+/); 
       $trNew.find("td:last").html('<a href="#" class="remove">Remove</a>'); 
       $.each($trNew.find(':input'), function (i, val) { 
        // Replaced Name 
        var oldN = $(this).attr('name'); 
        var newN = oldN.replace('[' + suffix + ']', '[' + (parseInt(suffix) + 1) + ']'); 
        $(this).attr('name', newN); 
        //Replaced value 
        var type = $(this).attr('type'); 
        if (type.toLowerCase() == "text") { 
         $(this).attr('value', ''); 
        } 

        // If you have another Type then replace with default value 
        $(this).removeClass("input-validation-error"); 

       }); 
       $trLast.after($trNew); 

       // Re-assign Validation 
       var form = $("form") 
        .removeData("validator") 
        .removeData("unobtrusiveValidation"); 
       $.validator.unobtrusive.parse(form); 
      }); 

      // 2. Remove 
      $('a.remove').live("click", function (e) { 
       e.preventDefault(); 
       $(this).parent().parent().remove(); 
      }); 

     }); 
       </script> 
      } 
+0

Что такое 'return RedirectToAction (« Создать »,« Вопрос », вопрос),' перенаправление на? Ни один из ваших контроллеров не принимает в качестве параметра «Вопрос». Также, вообще говоря, вы должны создать ViewModel для своей страницы с помощью SelectList в нем вместо сохранения SelectList в ViewBag. – alex

+0

Что касается редактирования, вы все еще не создаете свои элементы управления правильно - вам нужно использовать 'for' loop -' for (int i = 0; i m [i] .CorrectOrNot) ...} ' –

ответ

3

Для ModelBinder привязать к списку форма HTML должны быть последовательно индексируются.

Ваш

<td>@Html.CheckBoxFor(model=>a.CorrectOrNot)</td> 
<td>@Html.EditorFor(model=>a.AnswerText)</td> 
<td>@Html.EditorFor(model=>a.OpenAnswerText)</td> 

создает то, что будет связано с индивидуальным ответом. Вам нужно, чтобы сделать HTML, который будет привязан к списку, что-то вроде

@for (int i = 0; i < ((List<Answer>)ViewData["Answers"]).Count; i++) 
{ 
    <tr> 
     <td>@Html.CheckBoxFor(model=>((List<Answer>)ViewData["Answers"])[i].CorrectOrNot)</td> 
     <td>@Html.EditorFor(model=>((List<Answer>)ViewData["Answers"])[i].AnswerText)</td> 
     <td>@Html.EditorFor(model=>((List<Answer>)ViewData["Answers"])[i].OpenAnswerText)</td> 
    </tr> 
} 

Кроме того, это выглядит довольно ужасно произнесения ViewData повсюду. Было бы лучше, если бы вы планировали сохранить этот подход, создав реальную модель представления. Вы можете передать эту модель в представление, и она может обернуть коллекции вопросов и ответов.

EDIT:

Вы все еще должны иметь последовательный индекс против вашего списка, который отредактированная реализация не поставляя. Что-то вроде

@for (int i = 0; i < Model.Answers.Count; i++) 
{ 
    <tr> 
    <td>@Html.CheckBoxFor(model=> Model.Answers[i].CorrectOrNot)</td> 
    <td>@Html.EditorFor(model=> Model.Answers[i].AnswerText)</td> 
    <td>@Html.EditorFor(model=> Model.Answers[i].OpenAnswerText)</td> 
    </tr> 
} 
+0

В этой статье также объясняется, как вы могли бы добиться этого с помощью несекретных индексов, а также последовательных http://haacked.com/archive/2008/10/23/model- связывание-к-а-list.aspx / – mheptinstall

0

ViewData только для передачи данных между представлением и контроллером. Вы можете использовать сеанс для передачи данных между контроллером

1

ViewData имеет значение при переходе от контроллера к виду. Он не будет отправлять назад.

Вы должны ретранслировать на (модель/параметра) связывания, который будет заботиться о прохождении List<Answer> answerList для вас

0

Спасибо за комментарии. Они действительно помогли мне. Все было правильно, что вы говорите, но было что-то, чего не хватало. Мой IEnumerable в ViewModel просто не позволяет мне индексировать мои значения, вместо этого использование IList помогло мне индексировать все, как это должно быть, и все работает.