表格提交时模型集合丢失

本文关键字:集合 模型 提交 表格 | 更新日期: 2023-09-27 18:06:47

使用模型下面的post请求为两个集合返回null,但它正确返回布尔属性。我的期望是,在get请求期间加载到模型中的集合将持续到post请求。我错过了什么?

编辑:基本上,我正在尝试根据用户选择的selectlist和复选框来更新发票列表。

控制器:

    [HttpGet]
    [AllowAnonymous]
    public async Task<ActionResult> Index(bool displayFalse = true)
    {
        InvoiceViewModel invoiceView = new InvoiceViewModel();
        var companies = new SelectList(await DbContext.Company.ToListAsync(), "CompanyID", "Name").ToList();
        var invoices = await DbContext.Invoice.Where(s => s.Paid.Equals(displayFalse)).ToListAsync();
        return View(new InvoiceViewModel { Companies = companies,Invoices = invoices, SelectedCompanyID = 0, DisplayPaid = displayFalse});
    }
    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> Index(InvoiceViewModel model)
    {
        model.Invoices = await DbContext.Invoice.Where(s => s.CompanyID.Equals(model.SelectedCompanyID) && s.Paid.Equals(model.DisplayPaid)).ToListAsync();
        return View(model);         
    }

模型:

public class InvoiceViewModel
{
    public int SelectedCompanyID { get; set; }
    public bool DisplayPaid { get; set; }
    public ICollection<SelectListItem> Companies { get; set; }
    public ICollection<Invoice> Invoices{ get; set; }
}

视图:

@model InvoiceIT.Models.InvoiceViewModel
<form asp-controller="Billing" asp-action="Index" method="post" class="form-horizontal" role="form">
<label for="companyFilter">Filter Company</label>
<select asp-for="SelectedCompanyID"  asp-items="Model.Companies"  name="companyFilter"  class="form-control"></select>
<div class="checkbox">
    <label>
        <input type="checkbox" asp-for="DisplayPaid" />Display Paid  
        <input type="submit" value="Filter" class="btn btn-default" />     
    </label>
</div>
<br />
</form>
<table class="table">
<tr>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceID)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().CompanyID)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Description)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().DueDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Paid)
    </th>
    <th></th>
</tr>
@foreach (var item in Model.Invoices)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.InvoiceID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CompanyID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Description)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.InvoiceDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.DueDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Paid)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id = item.InvoiceID }) |
            @Html.ActionLink("Details", "Index", "InvoiceItem", new { id = item.InvoiceID }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.InvoiceID })
        </td>
    </tr>
}
</table>

表格提交时模型集合丢失

表单只返回控件的名称/值对(input、textarea、select)。由于您生成的两个控件是针对模型的SelectedCompanyIDDisplayPaid属性的,因此只有这些属性将在发布时被绑定。

从您的评论来看,您真正想做的是根据所选公司和复选框的值更新发票表。

从性能的角度来看,方法是使用ajax根据控件的值来更新发票表。

创建一个新的控制器方法,返回表行的部分视图

public PartialViewResult Invoices(int CompanyID, bool DisplayPaid)
{
  // Get the filtered collection
  IEnumerable<Invoice> model = DbContext.Invoice.Where(....
  return PartialView("_Invoices", model);
}

注意,如果您希望最初显示未过滤的结果,您可能希望使CompanyID参数为空并调整查询

和局部视图_Invoices.cshtml

@model IEnumerable<yourAssembly.Invoice>
@foreach(var item in Model)
{
  <tr>
    <td>@Html.DisplayFor(m => item.InvoiceID)</td>
    .... other table cells
  </tr>
}

在主视图

@model yourAssembly.InvoiceViewModel
@Html.BeginForm()) // form may not be necessary if you don't have validation attributes
{
  @Html.DropDownListFor(m => m.SelectedCompanyID, Model.Companies)
  @Html.CheckboxFor(m => m.DisplayPaid)
  <button id="filter" type="button">Filter results</button>
}
<table>
  <thead>
    ....
  </thead>
  <tbody id="invoices">
    // If you want to initially display some rows
    @Html.Action("Invoices", new { CompanyID = someValue, DisplayPaid = someValue })
  </tbody>
</table>
<script>
  var url = '@Url.Action("Invoices")';
  var table = $('#invoices');
  $('#filter').click(function() {
    var companyID = $('#SelectedCompanyID').val();
    var isDisplayPaid = $('#DisplayPaid').is(':checked');
    $.get(url, { CompanyID: companyID, DisplayPaid: isDisplayPaid }, function (html) {
      table.append(html);
    });
  });
</script>

另一种选择是按原样发布表单,但不是返回视图,而是使用

return RedirectToAction("Invoice", new { companyID = model.SelectedCompanyID, DisplayPaid = model.DisplayPaid });

并修改GET方法以接受附加参数。

边注:您使用TagHelpers来生成
select asp-for="SelectedCompanyID"  asp-items="Model.Companies"  name="companyFilter"  class="form-control"></select>

我对它们不太熟悉,但如果name="companyFilter"工作(并覆盖默认名称name="SelectedCompanyID"),那么你生成的name属性与你的模型属性不匹配,因此SelectedCompanyID将是0(默认为int)在POST方法。

ToList()附加到填充companies的语句中是将SelectList转换为List<T>,窗体将不识别为SelectList。此外,通过使用动态var关键字,您可以屏蔽此问题。试试这个:

SelectList companies = new SelectList(await DbContext.Company.ToListAsync(), "CompanyID", "Name");

一般来说,尽量避免使用var,除非该类型是真正动态的(在运行时之前未知)。

您将模型数据置于表单之外,因此它不会提交!

    <form asp-controller="Billing" asp-action="Index" method="post" class="form-horizontal" role="form">
<label for="companyFilter">Filter Company</label>
<select asp-for="SelectedCompanyID"  asp-items="Model.Companies"  name="companyFilter"  class="form-control"></select>
<div class="checkbox">
    <label>
        <input type="checkbox" asp-for="DisplayPaid" />Display Paid  
        <input type="submit" value="Filter" class="btn btn-default" />     
    </label>
</div>
<br />
<table class="table">
<tr>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceID)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().CompanyID)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Description)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().DueDate)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Paid)
    </th>
    <th></th>
</tr>
@foreach (var item in Model.Invoices)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.InvoiceID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CompanyID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Description)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.InvoiceDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.DueDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Paid)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id = item.InvoiceID }) |
            @Html.ActionLink("Details", "Index", "InvoiceItem", new { id = item.InvoiceID }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.InvoiceID })
        </td>
    </tr>
}
</table>
</form>

使用for循环来创建与公司相关的列表,可以映射回并保存公司值

for(c = 0 ; c < Model.Companies.Count(); c++)
{
    <input type='hidden' name='@Html.NameFor(Model.Companies[c].Propery1)'       id='@Html.IdFor(Model.Comapnies[c].Propery1)' value='somevalue'>someText />
 <input type='hidden' name='@Html.NameFor(Model.Companies[c].Propery2)'                id='@Html.IdFor(Model.Comapnies[c].Propery2)' value='somevalue'>someText />
}

这确保列表被映射回默认的模型绑定器期望列表是ListProperty[index]格式