能够动态地将子列表项添加到ViewModel

时间:2016-11-13 14:50:31

标签: javascript c# asp.net-mvc asp.net-mvc-5

背景

我正在建立一个相当简单的库存和订单管理系统。在所有这样的系统中,您最终会得到订单(在我的情况下为PurchaseOrder)有一个订单行项目列表(在我的应用程序中称为LineItem)。

后端代码

现在,我的ViewModel直接映射到我的实体,所以我的PurchaseOrderVm看起来像这样:

public class PurchaseOrderVm
{
  public PurchaseOrderVm()
  {
    LineItems = new List<LineItemVm>();
  }

  public int Id { get; set; }
  public DateTime Date { get; set; }
  public PurchaseOrderStatus Status { get; set; }
  [Display(Name = "Shipping Cost")]
  public decimal ShippingCost { get; set; }
  [Display(Name = "Import VAT")]
  public decimal Vat { get; set; }
  [Display(Name = "Import Duty")]
  public decimal ImportDuty { get; set; }
  [Display(Name = "Supplier Id")]
  public string SupplierId { get; set; }
  [Display(Name = "Supplier")]
  public string SupplierName { get; set; }
  public IEnumerable<LineItemVm> LineItems { get; set; }

  public List<SelectListItem> Suppliers { get; set; }

  public string ButtonText => Id != 0 ? "Update Purchase Order" : "Add Purchase Order";
}

我的LineItemVm看起来像这样:

public class LineItemVm
{
  public int Id { get; set; }
  public int Quantity { get; set; }
  public int OrderId { get; set; }
  [Display(Name = "Product")]
  public int ProductId { get; set; }
}

前端/用户体验

我知道我想要构建的体验类型,但我不确定自MVC3以来这方面有多少事情发生了变化。我想构建这个(突出显示重要性的位):

order management

试过

我只能通过这样做才能让这个工作为LineItem

<input class="form-control " id="ProductId" name="LineItems[0].ProductId" type="number" value="">

但显然这与动态相反。允许用户按照自己的意愿添加和删除项目并使表单和模型绑定器仍然有效的最简洁方法是什么?

1 个答案:

答案 0 :(得分:2)

您基本上可以使用客户端javascript代码构建新的订单项行。您需要确保输入元素(产品和数量)具有正确的名称属性值,以便模型绑定起作用。

要使模型绑定正常工作,您的输入名称应该是LineItems[0].ProductIdLineItems[0].Quantity(对于第1行),LineItems[1].ProductIdLineItems[1].Quantity(对于第2行)等。

由于您需要每行的产品下拉列表,我们将在页面中保留一个select元素,每当我们需要添加新的订单项行时,我们将克隆此元素并将其用于该行。我们将使用css隐藏此选择,因为这更像是我们根据需要克隆的查找数据。

@model PurchaseOrderVm 
@using (Html.BeginForm("Add","Product"))
{
    @Html.LabelFor(f=>f.SupplierName)
    @Html.TextBoxFor(s=>s.SupplierName)

    <label>Order Products</label>
    <table id="items">
        <thead>
            <tr>
                <th>Product</th><th>Quantity</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <button id="addProduct">Add Product</button>
    <input type="submit"/>
}
<SELECT Id="Products">
    <option value="1">Product 1</option>
    <option value="2">Product 2</option>
    <option value="3">Product 3</option>
</SELECT>

我硬编码了产品选择元素的内容。您可以使用Html.DropDownListFor / Html.DropDownList辅助方法替换来自服务器的数据

现在,听取addProduct按钮上的click事件,创建一个新行,添加select元素和数量输入元素并将其附加到表中。单击删除链接时,只需删除最近的行并重新组织我们创建的其他输入的名称值。

$(function () {          

    $('#addProduct').click(function (e) {
        e.preventDefault();
        var rowIndex = $("#items>tbody>tr").length;
        var newRow = $("<tr/>");
        var p = $("#Products").clone()
              .attr("name", "LineItems[" + rowIndex + "].ProductId")
              .data("n","LineItems[r].ProductId").show();

        $("<td/>").append(p).appendTo(newRow);
        var q = $("<input />")
             .attr("name", "LineItems[" + rowIndex + "].Quantity")
             .data("n","LineItems[r].Quantity");
        var r = $("<a/>").attr("class", "remove").text("Remove");
        $("<td/>").append(q).append(r).appendTo(newRow);

        $("#items>tbody").append(newRow);

    });

    $("#items").on("click",".remove",function(e) {
        e.preventDefault();
        $(this).closest("tr").remove();
        //Re organize the input names
        $("#items>tbody>tr").each(function (rowIndex, row) {
            $(row).find("input").each(function(a, b) {
                var newName = $(b).data("n").replace("r", rowIndex);
                $(b).attr("name", newName);
            });
        });
    });

});

现在提交表单时,视图模型的LineItems属性将具有正确的数据。

[HttpPost]
public ActionResult Add(PurchaseOrderVm model)
{
  // check model.LineItems
  // to do : Return something
}