[英]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! 干杯!
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>
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> }
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.