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

标签 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="">

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

最佳答案

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

要使模型绑定(bind)起作用,您的输入名称应类似于 LineItems[0].ProductIdLineItems[0].Quantity(对于第 1 行)、 LineItems[1].ProductIdLineItems[1].Quantity(对于第 2 行)等

由于每一行都需要一个产品下拉列表,我们将在页面中保留一个选择元素,每当我们需要添加一个新的订单项行时,我们将克隆该元素并将其用于该行。我们将使用 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 元素和 quantity input 元素并将其追加到表中。单击删除链接时,只需删除最近的行并重新组织我们创建的其他输入的名称值。

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

});

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

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

关于javascript - 能够动态地将子列表项添加到 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40575124/

相关文章:

c# - 如何根据字符串描述符的一部分对列表 <string> 进行排序

c# - ASP.net MVC 路由参数

c# - 如何重命名在 ASP.NET 5 Web 项目中用作 Web 根目录的 "wwwroot"文件夹

c# - 如何从 MVC Controller 打开新窗口?

javascript - iPad 闪存更换最佳实践

javascript - 类型错误 : Cannot read property '49' of undefined; for MachineTyping effect component in React

c# - EF实体在更新时不保存相同实体类型的子属性

javascript - jQuery ajax ".done"回调未触发

javascript - 在 for 循环中链接 Promise 的最佳方式是什么?

c# - WPF 页面之间的切换