简体   繁体   English

复杂的ViewModel。 视图返回一个空的ViewModel。 [HttpPost]动作

[英]Complex ViewModel. View returns an empty ViewModel. [HttpPost] Action

I have a problem with the action that recives a complex ViewModel by POST and all of its object components are null, even though I have initialized them in Action and have returned the whole ViewModel to the View using GET method. 我有一个问题,该问题通过POST接收到一个复杂的ViewModel,并且它的所有对象组件都是null,即使我已在Action中对其进行了初始化,并已使用GET方法将整个ViewModel返回给View。

Let me explain the situation. 让我解释一下情况。 I have a complex model for a View that consists of three sections: Applicant details, Application details, and a list of Recordings . 我有一个包含三个部分的View复杂模型: Applicant详细信息, Application详细信息和Recordings列表。 This View is complex to (1) let me see the details of Applicant I am creating application for, (2) have a list of recordings I would like to choose from which then I can add to Application . 此视图的复杂性在于(1)让我看到要为其创建应用程序的Applicant的详细信息,(2)列出了我想要选择的记录,然后可以将其添加到Application This is my ViewModel: 这是我的ViewModel:

public class ApplicantApplicationRecordingsViewModel
{
    // Applicant
    public Applicant Applicant { get; set; }

    // Application
    public Application Application { get; set; }
    public SelectList UsageTypeSelectList { get; private set; }
    public SelectList UsageEndAppSelectList { get; private set; }

    // Recordings
    public IEnumerable<RecordingViewModelApp>
                                RecordingsViewModelApp { get; set; }

    public ApplicantApplicationRecordingsViewModel()
        : this(new MyDBContext())
    {
    }
    public ApplicantApplicationRecordingsViewModel(MyDBContext dbContext)
    {
            PopulateUsageTypeSelectList(dbContext);
            PupulateUsageEndAppSelectList(dbContext);
    }

    private void PopulateUsageTypeSelectList(MyDBContext dbContext,
                                int? usageTypeSelected = null)
    {
        IEnumerable<UsageType> utQuery =
                dbContext.UsageTypes.OrderBy(
                ut => ut.UsageTypeName).ToList();
        this.UsageTypeSelectList =
                new SelectList(utQuery,
                    "UsageTypeID",
                    "UsageTypeName",
                    usageTypeSelected);
    }
    private void PupulateUsageEndAppSelectList(
                                MyDBContext dbContext,
                                int? usageEndAppSelected = null)
    {
        IEnumerable<UsageEndApp> ueaQuery =
                dbContext.UsageEndApps.OrderBy(uea => uea.UsageEndAppName).ToList();
        this.UsageEndAppSelectList =
                new SelectList(ueaQuery,
                    "UsageEndAppID",
                    "UsageEndAppName",
                    usageEndAppSelected);
    }
}

In the controller I simply populate a list of recordings for RecordingViewModelApp , put details of an applicant to Applicant and leave the Application object empty to be filled in a View. 在控制器中,我只需填充RecordingViewModelApp的记录列表,将申请人的详细信息放到Applicant ,并将Application对象留空以填充到视图中。

    public ActionResult Create(int? ApplicantID)
    {
        if (ApplicantID == null)
        {
            // Error 400. Bad Request Exception
        }
        ApplicantApplicationRecordingsViewModel viewModel = null;
        using (MyDBContext dbContext = new MyDBContext())
        {
            Applicant applicant =
                    dbContext.Applicants.Find(ApplicantID);
            if (applicant == null)
            {
                // Error 404. Http not found
            }
            List<RecordingViewModelApp> recordings =
                    getViewModel(
                            dbContext.Recordings.ToList(),
                            dbContext);
            viewModel =
                    new ApplicantApplicationRecordingsViewModel(dbContext);
            viewModel.Applicant = applicant;
            viewModel.RecordingsViewModelApp = recordings;
        }
        return View(viewModel);
    }

The problem is that when I return the ViewModel ( ApplicantApplicationRecordingsViewModel ) back to the [HttpPost] Create() Action, all the View Model's components are null, eg the list of RecordingViewModelApp is null. 问题是,当我将ViewModel( ApplicantApplicationRecordingsViewModel )返回到[HttpPost] Create()操作时,所有View Model的组件都为null,例如RecordingViewModelApp的列表为null。 What Am I missing? 我想念什么? I would need to understand what's going on behind the scene and why default model binding doesn't work. 我需要了解幕后发生的事情以及为什么默认模型绑定不起作用。

[HttpPost]
[ActionName("Create")]
public ActionResult Create_post(
                    ApplicantApplicationRecordingsViewModel viewModelToValidate)
{
    // Validation against Application only and TryToUpdate() etc.
}

CHeers! 干杯!


EDIT: 编辑:

The View 风景

 @model Project.ApplicantApplicationRecordingsViewModel @{ string applicantDetails = string.Format("{0} {1} {2}", Model.Applicant.title, Model.Applicant.firstName, Model.Applicant.lastName); ViewBag.Title = "Create a new application for " + applicantDetails; } <h2>@ViewBag.Title</h2> <hr /> @using (Html.BeginForm()) { <h3>Details of the applicant</h3> @Html.HiddenFor(item => Model.Applicant.ApplicantID) @Html.HiddenFor(item => Model.Application.ApplicationID) <table> <tr> <th>@Html.DisplayNameFor(item => Model.Applicant.title)</th> <th>@Html.DisplayNameFor(item => Model.Applicant.firstName)</th> <th>@Html.DisplayNameFor(item => Model.Applicant.lastName)</th> <th>@Html.DisplayNameFor(item => Model.Applicant.telephone)</th> <th>@Html.DisplayNameFor(item => Model.Applicant.mobile)</th> <th>@Html.DisplayNameFor(item => Model.Applicant.email)</th> </tr> <tr> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.title)</td> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.firstName)</td> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.lastName)</td> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.telephone)</td> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.mobile)</td> <td class="display-field">@Html.DisplayFor(item => Model.Applicant.email)</td> </tr> </table> <hr /> // ---------------------------------------------------------------------------------------------- <h3>Details of the application</h3> <table id="main"> <tr> <td> <table> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.ApplicationNo)</td> <td class="editor-field"> @Html.EditorFor(item => Model.Application.ApplicationNo) @Html.ValidationMessageFor(item => Model.Application.ApplicationNo) </td> </tr> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.StartDate)</td> <td class="editor-field"> @Html.EditorFor(item => Model.Application.StartDate) @Html.ValidationMessageFor(item => Model.Application.StartDate) </td> </tr> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.EndDate)</td> <td class="editor-field"> @Html.EditorFor(item => Model.Application.EndDate) @Html.ValidationMessageFor(item => Model.Application.EndDate) </td> </tr> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.UsageTypeID)</td> <td class="editor-field"> @Html.DropDownListFor(item => Model.Application.UsageTypeID, Model.UsageTypeSelectList, "-- Select Usage --") @Html.ValidationMessageFor(item => Model.Application.UsageTypeID) </td> </tr> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.UsageEndAppID)</td> <td class="editor-field"> @Html.DropDownListFor(item => Model.Application.UsageEndAppID, Model.UsageEndAppSelectList, "-- Select Type --") @Html.ValidationMessageFor(item => Model.Application.UsageEndAppID) </td> </tr> <tr> <td class="editor-label first-label">@Html.DisplayNameFor(item => Model.Application.linkToPaperVer)</td> <td class="editor-field"> @Html.EditorFor(item => Model.Application.linkToPaperVer) @Html.ValidationMessageFor(item => Model.Application.linkToPaperVer) </td> </tr> </table> </td> <td class="editor-label"> @Html.DisplayNameFor(item => Model.Application.Info) </td> <td class="editor-field"> @Html.EditorFor(item => Model.Application.Info) @Html.ValidationMessageFor(item => Model.Application.Info) </td> </tr> </table> <hr /> // ---------------------------------------------------------------------------------------------- <h3>List of recordings</h3> Html.RenderPartial("~/Views/Recordings/_List_App.cshtml", Model.RecordingsViewModelApp); <hr /> // ---------------------------------------------------------------------------------------------- <p> <input type="submit" value="Create" /> </p> } <div> @Html.ActionLink("Back to List", "Index", "Applicants") </div> 


EDIT 2 编辑2

PartialView: PartialView:

 @model IEnumerable<Project.ViewModels.RecordingViewModelApp> @if (Model != null) { <div> <table class="data-in-table"> <tr> <th>@Html.DisplayNameFor(model => model.IsSelected)</th> <th>@Html.DisplayNameFor(model => model.FileLocation)</th> <th>@Html.DisplayNameFor(model => model.EnteredDate)</th> <th>@Html.DisplayNameFor(model => model.Duration)</th> <th>@Html.DisplayNameFor(model => model.Status)</th> </tr> @foreach (var item in Model) { <tr> <td class="display-field">@Html.EditorFor(model => item.IsSelected)</td> <td class="display-field">@Html.DisplayFor(model => item.FileLocation)</td> <td class="display-field">@Html.DisplayFor(model => item.EnteredDate)</td> <td class="display-field">@Html.DisplayFor(model => item.Duration)</td> <td class="display-field">@Html.DisplayFor(model => item.Status)</td> </tr> } </table> </div> } else { <h3>No recordings attached to this Patient</h3> } 


EDIT 3 编辑3

The RecordingViewModelApp : RecordingViewModelApp

 public class RecordingViewModel { public int RecordingID { get; set; } public string FileLocation { get; set; } public DateTime EnteredDate { get; set; } public int Duration { get; set; } public string Status { get; set; } } public class RecordingViewModelApp : RecordingViewModel { public bool IsSelected { get; set; } } 

First to fix the view model. 首先要修复视图模型。 A view model should only contain simple properties representing what you want to display and/or edit 视图模型应仅包含表示要显示和/或编辑的内容的简单属性。

View model 查看模型

public class ApplicantApplicationRecordingsViewModel
{
  public Applicant Applicant { get; set; }
  public Application Application { get; set; }
  public IEnumerable<RecordingViewModelApp> Recordings { get; set; }
  public string Title { get; set; }
  public SelectList UsageTypeSelectList { get; private set; }
  public SelectList UsageEndAppSelectList { get; private set; }
}

Controller (note validation checks omitted) 控制器(省略注释验证检查)

public ActionResult Create(int ApplicantID) // assume you must have a custom route for this?
{
  ApplicantApplicationRecordingsViewModel viewModel = new ApplicantApplicationRecordingsViewModel();
  Applicant applicant = dbContext.Applicants.Find(ApplicantID);
  viewModel.Applicant = applicant;
  viewModel.Title = string.Format("Create a new application for {0} {1} {2}", applicant.title, applicant.firstName, applicant.lastName);
  viewModel.Recordings = getViewModel(dbContext.Recordings.ToList(), dbContext); // not sure what this is?
  viewModel.UsageTypeSelectList = new SelectList(dbContext.UsageTypes.OrderBy(ut => ut.UsageTypeName), "UsageTypeID", "UsageTypeName");
  viewModel.UsageEndAppSelectList = new SelectList(dbContext.UsageEndApps.OrderBy(uea => uea.UsageEndAppName), "UsageEndAppID", "UsageEndAppName");  
  return View(viewModel);
}  

View 视图

@model Project.ApplicantApplicationRecordingsViewModel
<h2>@Model.Title</h2>
@using (Html.BeginForm())
{
  @Html.HiddenFor(item => Model.Applicant.ApplicantID) // include for post back but Application.ApplicationID not necessary (its a new application!)
  <h3>Details of the applicant</h3>
  // Add display detail for applicant, but use css for layout (position, floats etc), not tables (which are for tabular data)
  <h3>Details of the application</h3>
  // Add controls for Application but use LabelFor() so the label is associated with the control (otherwise its not a label)
  @Html.DisplayNameFor(m => m.Application.ApplicationNo)
  @Html.EditorFor(m => m.Application.ApplicationNo) 
  @Html.ValidationMessageFor(m => m.Application.ApplicationNo)
  ....
  <h3>List of recordings</h3>
  <table>
    <thead>
      .... // add table headings
    </thead>
    <tbody>
      @Html.EditorFor(m => m.Recordings) // This uses a custom editor template to display and select recordings
    </tbody>
  </table>
  <input type="submit" value="Create" />
}

EditorTemplate ( /Views/Shared/EditorTemplates/RecordingViewModelApp.cshtml ) EditorTemplate( /Views/Shared/EditorTemplates/RecordingViewModelApp.cshtml

Note you must use either a for loop or a custom EditorTemplate to render collections. 请注意,您必须使用for循环或自定义EditorTemplate来呈现集合。 The foreach loop you used just renders duplicate id (invalid html) and name attributes without the correct indexers so will not post back to a collection. 您使用的foreach循环仅呈现重复的id (无效的html)和name属性,而没有正确的索引器,因此不会将其发回到集合中。

@model RecordingViewModelApp
<tr>
  <td class="display-field">
    @Html.CheckBoxFor(m => m.IsSelected) // required for postback
    @Html.HiddenFor(m => m.RecordingID) // required for postback
  </td>
  <td class="display-field">@Html.DisplayFor(m => m.FileLocation)</td>
  .... // other display properties
</tr>

POST method POST方法

[HttpPost]
public ActionResult Create(ApplicantApplicationRecordingsViewModel model)
{
  // model is now bound with the Applicant ID, all the properties of Application
  // and the collection of Recordings with their ID and IsSelected property.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM