背景

我正在构建一个相当简单的库存和订单管理系统。在所有此类系统中,您最终都可以了解一个订单(在我的情况下为PurchaseOrder)具有一个订单行项目列表(在我的应用程序中称为LineItem)。

后端代码

现在,我的ViewModels直接映射到我的实体,所以我的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; }
}


前端/ UX

我知道我想积累的经验类型,但是我不确定自MVC3以来在这方面发生了多少变化。我要构建此文件(强调重要性的位):

javascript - 能够将子列表项动态添加到ViewModel-LMLPHP

试过了

我只能通过以下操作使它在单个LineItem下工作:

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


但这显然与动态相反。允许用户根据需要添加和删除项目并使表格和模型绑定程序仍然有效的最干净的方法是什么?

最佳答案

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

为了使模型绑定生效,您的输入名称应类似于LineItems[0].ProductIdLineItems[0].Quantity(对于row1),LineItems[1].ProductIdLineItems[1].Quantity(对于row2)等。

由于您需要为每行添加一个产品下拉菜单,因此我们将在页面中保留一个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>


我对产品select元素的内容进行了硬编码。您可以使用Html.DropDownListFor / Html.DropDownList帮助器方法将其替换为来自服务器的数据

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

$(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
}

08-26 18:55