提交表单时模型集合丢失

时间:2015-08-06 05:07:32

标签: c# asp.net-mvc asp.net-core

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

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

控制器:

    [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>

4 个答案:

答案 0 :(得分:4)

表单仅回发其控件的名称/值对(input,textarea,select)。由于您生成的唯一2个控件是针对模型的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将是POST方法中的0int的默认值)。

答案 1 :(得分:0)

ToList()附加到填充companies的语句正在将SelectList转换为List<T>,表单将无法识别为SelectList。此外,通过使用动态var关键字,您正在屏蔽此问题。试试这个:

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

一般情况下,尽量避免使用var,除非该类型是真正动态的(在运行时才知道)。

答案 2 :(得分:0)

您将模型数据放在表单之外,因此不会提交!

    <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>

答案 3 :(得分:-1)

使用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]格式