как я могу создать несколько форм, каждая из которых будет создана из списка элементов? - PullRequest
0 голосов
/ 12 июля 2020

В приложении MVC у меня есть список экзаменационных вопросов, и я хочу представить небольшое количество из них пользователю на той же странице, но где каждый ответ может быть представлен отдельно. Моя страница выглядит так ... enter image description here

The view code is ....

    @model List<QuestionResponseVM>
    @for (int i = 0; i < Model.Count(); i++)
    {
      using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
      {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(model => model[i].QuestionID)
        <tr>
          <td width="35%">
            @Html.Raw(Model[i].QuestionText)
            @Html.HiddenFor(model => model[i].QuestionText)
          </td>
          <td>
            @Html.TextAreaFor(model => model[i].Response, new { @name = "DisplayTextEdit", @id = "DisplayTextEdit", @rows = 1, @cols = 80 })
          </td>
          <td width="30%">
            <div class="form-group">
              <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-primary" />
              </div>
            </div>

          </td>
        </tr>
      }
    }

Моя проблема в том, что я могу получить данные, возвращенные методу POST только для вопроса 1. Вот код контроллера ....

  public class ChecksController : Controller  
  {  
  
    public ActionResult CheckQuestions()
    {
      return View(LoadQuestions());
    }
  
    // POST: Checks
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CheckQuestions(List<QuestionResponseVM> questResponses)
    {
      List<QuestionResponseVM> testList = new List<QuestionResponseVM>();
  
      if (ModelState.IsValid)
      {
        testList = LoadQuestions(questResponses[0].QuestionID, questResponses[0].Response);
      }
  
      return View(testList);
  
    }
  
    private List<QuestionResponseVM> LoadQuestions (int _QuestionID = -1, string _Response = "")
    {
      List<QuestionResponseVM> thisList = new List<QuestionResponseVM>();
  
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 1,
        QuestionText = "Question 1",
        Response = (_QuestionID == 1 ? _Response : "")
      });
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 2,
        QuestionText = "Question 2",
        Response = (_QuestionID == 2 ? _Response : "")
      });
      thisList.Add(new QuestionResponseVM()
      {
        QuestionID = 3,
        QuestionText = "Question 3",
        Response = (_QuestionID == 3 ? _Response : "")
      });
  
      return thisList;
    }
  }
  1. Если метод POST контроллера имеет параметр QuestionResponseVM questResponses , чего я ожидал (надеясь на ), то из представления возвращается значение null независимо от того, какая кнопка «Сохранить» была нажата.

  2. Однако, если я изменю параметр на список (например, List questResponses ), то кнопка «Сохранить» для вопроса 1 возвращает список с одним элементом и правильными данными. Но любая другая кнопка «Сохранить» (например, вопрос 2 или вопрос 3) возвращает пустой список.

Поведение для сценария 1. кажется мне нелогичным, так как «Начать форму» "установлен для возврата одного элемента модели (экземпляра модели), то есть" Модель [i] ". А в сценарии 2. я просто не понимаю, почему он работает для первой формы (кнопка «Сохранить»), но не работает для других.

Я не думаю, что мне нужно использовать JScript или AJAX сделать это.

Но ясно, что я не «соединяю точки» здесь. Может кто-нибудь объяснить мое наблюдаемое поведение и, возможно, дать мне pu sh в правильном направлении, чтобы выполнить это требование?

Я был бы очень признателен за любую помощь.

Ответы [ 2 ]

0 голосов
/ 13 июля 2020

Ваша кнопка сохранения в вопросе 1 отправляет форму контроллеру. Вам нужно будет либо иметь одну кнопку «Сохранить / отправить» в конце набора вопросов и использовать объект FormCollection, либо потратить время на настройку JQuery / Ajax для событий щелчка на каждой кнопке и удаление элемента формы. У вас может быть и то, и другое, если кнопка внизу станет «Далее», а затем будет отправлена ​​контроллеру для получения следующего набора связанных вопросов.

0 голосов
/ 12 июля 2020

Прежде чем ответить на ваши вопросы, я не понимаю, что new {questResponses = Model[i] })) делает в ваших формах:

using (Html.BeginForm("CheckQuestions", "Checks", FormMethod.Post, new {questResponses = Model[i] }))
{
    ...
}

Model[i] - сложный объект. Все, что у вас было, это имя объекта:

enter image description here


Q1: If the Controller POST method has just a single parameter

Since you're using a for loop to generate each form and inputs within the form, the name of those inputs will be in the forms of [INDEX].NAME:

enter image description here

By default, the model binding will bind those inputs (QuestionId, QuestionText and Response) to a matching object. QuestionResponseViewModel indeed matches that. The problem is [INDEX]. prefix.

In order for the default model binding to work, the parameter name you declare in the POST method has to be called [INDEX], i.e., [0] for the first form, [1] for the second form and so on:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM [0])
{
    ...
}

Но вы знаете, что мы не можем объявить ничего подобного в C# .

Исправление для Q1

Вместо использования обычного для l oop, вы можете использовать foreach для генерации каждой формы. Таким образом, вы избавляетесь от необходимости именовать параметр, который изменяется для каждой формы.

Еще один «GOTYOU» здесь заключается в том, что параметр в контроллере должен соответствовать переменной, которую вы объявлено в for l oop для каждого QuestionResponseViewModel:

@foreach (var qrVM in Model)
{
    using(Html.BeginForm("..."))
    {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(x => qrVM.QuestionId)

        <tr>
            <td>
                @Html.DisplayFor(x => qrVM.QuestionId)
                @Html.HiddenFor(x => qrVM.QuestionId)
            </td>
            ...
        </tr>
    }
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CheckQuestions(QuestionResponseVM qrVM)
{
    // If you name the parameter something else, it won't bind!
    ...
}

Если вы думаете об этом, это имеет смысл, потому что вы знаете, что форма будет публиковать данные с такими ключами, как qrVM.QuestionId , qrVM.QuestionText обратно на сервер. Привязка модели по умолчанию будет искать модель, которая имеет эти свойства и называется qrVM.

введите описание изображения здесь

Q2: изменить параметр на список

Когда первая форма отправляет обратно на сервер, данные формы в теле запроса будут выглядеть так:

[0].RequestionId: 1
[0].RequestionText: Question 1
[0].Response: xxx

MVC модель привязка по-прежнему достаточно умна и считает, что вы публикуете первый элемент объявленного вами списка. Следовательно, вы увидите, что List<QuestionResponseVM> questResponses захватывает правильные данные для первой формы.

А как насчет второй и третьей формы? Если вы отправите данные, например, во вторую форму, данные формы в теле запроса будут выглядеть так:

[1].RequestionId: 2
[1].RequestionText: Question 2
[1].Response: xxx

MVC привязка модели видит его как второй элемент списка, но где 1-й элемент? И он запутался, поэтому не смог привязать данные к параметру. Следовательно, вы увидите NULL в параметре List<QuestionResponseVM> questResponses.

Мои 2 цента

  1. На самом деле вы не можете поместить форму внутри таблицы или между строками таблицы, как это. Считается недействительной структурой HTML. В любом случае использование таблиц в качестве структур для отображения данных на странице никогда не является хорошей идеей. Вместо этого вы можете использовать строку и столбцы Bootstrap.

  2. Я не знаю, почему и что заставило вас подумать, что AJAX вам не нужно. Ваш случай похож на лучший сценарий для go с подходом AJAX! Например, набрав AJAX, пользователь может сохранять ответ на каждый вопрос отдельно. Страницу не нужно обновлять.

...