如何正确使用 EditorFor 显示来自 AJAX 调用的复选框选择,以及 POST 选择?

How do I correctly use EditorFor to display checkbox selections from an AJAX call, and POST the selections?

我的问题有两个。

我有一个视图可以获取有关下拉列表变化的数据 selection。

检索到的数据是使用 Ajax 调用的 ViewModel class 的列表 属性。

此数据显示为 select 多个复选框,用户可以 select 任意数量的复选框。

如果我 return 来自 AJAX 调用的部分视图,这很容易,但根据我的经验,这不适用于 POST' 返回到控制器。没有正确绑定。

据我所知,正确的方法是使用 EditorFor,所以首先,我不知道如何从 AJAX 调用中填充 EditorFor。

其次,如果我通过从 GET 发送初始数据进行测试,EditorFor 会显示正确的复选框选项,但是当我 POST 时,项目数为 0。

查看:

@model EngineeringAssistantMVC.Controllers.FirmwareController.FirmwareViewModel

@using (Html.BeginForm("Upload", "Firmware", FormMethod.Post, new { @id = "uploadFirmwareForm", @class = "form-horizontal" }))
{

    <!-- Device -->
    <div class="form-group">
        <div class="col-lg-1">
            @Html.LabelFor(x => x.Device, htmlAttributes: new { @class = "control-label" })
         </div>

         <div class="col-lg-2">
             @Html.DropDownListFor(x => x.Device, ViewBag.Devices as IEnumerable<SelectListItem>, new { @class = "form-control", @id = "Devices" })
         </div>

         <div class="col-lg-9">
             @Html.ValidationMessageFor(x => x.Device, "", new { @class = "text-danger" })
         </div>
     </div>

    @Html.EditorFor(x => x.SelectedModels, "SelectedModels", new { @id = "Models" })
    @Html.HiddenFor(x => x.SelectedModels)

}

并且 AJAX 调用:

function GetModels() {
    $.ajax({
        type: "GET",
        url: '@Url.Action("GetModels", "Firmware", null)',
        data: { SelectedDevice: $('#Devices').val() },
        success: function (dataSet) {

            //$('#Models').html(dataSet);
            //$('#Models').data(dataSet);
            //$('#Models').val(dataSet);                

            // How do I populate the EditorFor from the dataSet returned?
        },
        error: function (err) {
            console.log("ERROR: " + err.responseText);
        },
    })
}

SelectedModels EditFor 模板:

@model IEnumerable<EngineeringAssistantMVC.ViewModels.ModelViewModel>
@foreach (var item in Model)
{
    @Html.CheckBoxFor(x => item.IsSelected)
    @Html.Label(item.Description)

    @Html.HiddenFor(x => item.ModelId)
    @Html.HiddenFor(x => item.IsSelected)
    @Html.HiddenFor(x => item.Description)
}

控制器:

[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadFile, FirmwareViewModel firmwareViewModel)
{
    // firmwareViewModel.SelectedModels count is 0 here
}

型号固件Class:

public class ModelFirmware
{
    public int ModelFirmwareId { get; set; }
    public int FirmwareId { get; set; }
    public int ModelId { get; set; }
} 

固件视图模型:

public class FirmwareViewModel
{
    public int FirmwareViewModelId { get; set; }

    [Required]
    public string Device { get; set; }
    public ICollection<ModelViewModel> SelectedModels { get; set; }
}

我无法让它正常工作。

编辑 1:- 添加 return 模型

的方法
[HttpGet]
    public ActionResult GetModels(string SelectedDevice)
    {
        var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
        var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();

    List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
    {
        ModelId = x.AnatomyModelId,
        Description = x.SerialPrefix,
        IsSelected = false,
    }).ToList();

    return Json(mvms);
}

您的代码有很多问题。

首先你没有正确使用 EditorTemplate。将其名称更改为 ModelViewModel.cshtml 以匹配 class 的名称,并将其定位在 /Views/Shared/EditorTemplates(或 /Views/YourControllerName/EditorTemplates)文件夹中。然后模板基于单个 object(另请注意创建与复选框关联的标签所需的 LabelFor(),并且您需要删除 IsSelected 的隐藏输入)

@model ModelViewModel

@Html.CheckBoxFor(m => m.IsSelected)
@Html.LabelFor(m => m.IsSelected, Model.Description)
@Html.HiddenFor(m => m.ModelId)
@Html.HiddenFor(m => m.Description)

另请参阅 Post an HTML Table to ADO.NET DataTable 以了解为什么您的 foreach 循环永远不会为模型绑定创建正确的 name 属性。

然后在主视图中使用

<div id="container">
    @Html.EditorFor(m => m.SelectedModels)
</div>

并删除 SelectedModels 的隐藏输入(但在您这样做之前,请检查该元素的 html 以了解其 value 永远不会绑定的原因)。 EditorFor() 方法将为 collection 中的每个项目正确生成 html。

接下来,将您的 GetModels() 方法更改为 return 基于 FirmwareViewModel 的部分视图,因为这就是您要发回的内容。请注意,您可以 return 一个 JsonResult,但这将意味着在 ajax 回调中生成大量 html 不会被强类型化。

[HttpGet]
public PartialViewResult GetModels(string SelectedDevice)
{
    var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
    var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
    List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
    {
        ModelId = x.AnatomyModelId,
        Description = x.SerialPrefix,
        IsSelected = false, // not really necessary since its the default
    }).ToList();
    FirmwareViewModel model = new FirmwareViewModel
    {
        SelectedModels = mvms
    };
    return PartialView(model);
}

您的 GetModels.cshtml 视图将是

@model FirmwareViewModel
@Html.EditorFor(m => m.SelectedModels)

然后,修改您的 ajax 调用以在成功回调中添加部分视图

$.ajax({
    type: "GET",
    url: '@Url.Action("GetModels", "Firmware")', // do not need to add 3rd parameter
    data: { SelectedDevice: $('#Devices').val() },
    success: function (response) {
        $('#container').html(response);
    },
    error: function (err) {
        console.log("ERROR: " + err.responseText);
    },
})

.html() 函数将替换 <div id="container"> 元素中已经存在的任何元素

最后,由于您使用的是视图模型,请充分利用它,不要使用 ViewBag。您的视图模型应包含一个 IEnumerable<SelectListItem> Devices 属性,您在 GET 方法中填充它(并在视图中使用 @Html.DropDownListFor(x => x.Device, Model.Devices, new { @class = "form-control" })(另请注意该方法生成 id="Device")。它应该还包含一个 HttpPostedFileBase 属性 以避免 POST 方法中的附加参数,并允许您添加验证属性。